@shbernal/ts-xlsx 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/core/address.js +0 -33
- package/dist/core/autofilter.js +0 -9
- package/dist/core/cell.js +0 -131
- package/dist/core/color-resolution.js +0 -60
- package/dist/core/column.js +0 -60
- package/dist/core/comment-thread.js +0 -45
- package/dist/core/conditional-formatting-overlay.js +0 -11
- package/dist/core/conditional-formatting.js +0 -10
- package/dist/core/containers.js +0 -15
- package/dist/core/data-validation-overlay.js +0 -25
- package/dist/core/data-validation.js +0 -7
- package/dist/core/date.js +0 -39
- package/dist/core/formula.js +0 -117
- package/dist/core/grid-edits.js +0 -38
- package/dist/core/image.js +0 -25
- package/dist/core/internal.js +0 -27
- package/dist/core/limits.js +0 -45
- package/dist/core/merge.d.ts +18 -0
- package/dist/core/merge.js +22 -7
- package/dist/core/modern-functions.js +0 -17
- package/dist/core/page-setup.js +0 -5
- package/dist/core/pivot-table.d.ts +1 -1
- package/dist/core/pivot-table.js +0 -48
- package/dist/core/preserved.js +0 -4
- package/dist/core/protection.js +0 -26
- package/dist/core/range.js +0 -112
- package/dist/core/row-input.d.ts +24 -0
- package/dist/core/row-input.js +29 -0
- package/dist/core/row.js +0 -66
- package/dist/core/style.js +0 -26
- package/dist/core/table-style.js +0 -28
- package/dist/core/table.js +0 -97
- package/dist/core/text-metrics.js +0 -40
- package/dist/core/theme.js +0 -85
- package/dist/core/value.js +0 -94
- package/dist/core/workbook-protection.js +0 -13
- package/dist/core/workbook.js +0 -380
- package/dist/core/worksheet-model.js +2 -23
- package/dist/core/worksheet.js +7 -506
- package/dist/customui/errors.js +0 -10
- package/dist/customui/index.js +0 -5
- package/dist/customui/ribbon.js +0 -43
- package/dist/entries/core.js +0 -6
- package/dist/entries/csv.js +0 -4
- package/dist/entries/customui.js +0 -2
- package/dist/entries/errors.js +0 -13
- package/dist/entries/vba.js +0 -5
- package/dist/entries/xlsb.js +0 -6
- package/dist/entries/xlsx.js +0 -13
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +5 -56
- package/dist/index.js +0 -12
- package/dist/io/csv/read.js +0 -18
- package/dist/io/csv/write.js +0 -18
- package/dist/io/opc/errors.js +0 -30
- package/dist/io/opc/inflate.js +0 -35
- package/dist/io/opc/namespaces.js +0 -15
- package/dist/io/opc/part-paths.js +0 -11
- package/dist/io/opc/read-opc.js +0 -31
- package/dist/io/opc/read-options.js +0 -6
- package/dist/io/opc/rels.js +0 -13
- package/dist/io/opc/sniff-format.js +0 -50
- package/dist/io/style/xf-style.js +0 -31
- package/dist/io/xlsb/errors.js +0 -10
- package/dist/io/xlsb/formula.js +1 -99
- package/dist/io/xlsb/primitives.js +1 -83
- package/dist/io/xlsb/ptg-functions.js +0 -34
- package/dist/io/xlsb/read-shared-strings.js +0 -7
- package/dist/io/xlsb/read-styles.js +3 -66
- package/dist/io/xlsb/read-worksheet.js +4 -87
- package/dist/io/xlsb/read.js +2 -60
- package/dist/io/xlsb/record-stream.js +0 -28
- package/dist/io/xlsb/record-types.js +0 -19
- package/dist/io/xlsx/cell-accumulator.js +0 -32
- package/dist/io/xlsx/cell-value.js +0 -29
- package/dist/io/xlsx/color-xml.d.ts +6 -0
- package/dist/io/xlsx/color-xml.js +42 -0
- package/dist/io/xlsx/comments.js +0 -84
- package/dist/io/xlsx/conditional-formatting.d.ts +1 -1
- package/dist/io/xlsx/conditional-formatting.js +1 -97
- package/dist/io/xlsx/data-validation.js +0 -56
- package/dist/io/xlsx/edit-vba.js +0 -54
- package/dist/io/xlsx/errors.js +0 -12
- package/dist/io/xlsx/hyperlinks.js +0 -40
- package/dist/io/xlsx/images.js +0 -32
- package/dist/io/xlsx/namespaces.js +0 -42
- package/dist/io/xlsx/package-plan.js +0 -47
- package/dist/io/xlsx/pivot.js +0 -22
- package/dist/io/xlsx/{pivot-read.js → read-pivot.js} +0 -30
- package/dist/io/xlsx/read-rows.js +1 -83
- package/dist/io/xlsx/{shared-strings-read.js → read-shared-strings.js} +0 -11
- package/dist/io/xlsx/read-styles.d.ts +25 -1
- package/dist/io/xlsx/read-styles.js +61 -108
- package/dist/io/xlsx/read-worksheet.js +1 -88
- package/dist/io/xlsx/read.js +3 -211
- package/dist/io/xlsx/relationships.js +0 -11
- package/dist/io/xlsx/rich-runs.js +0 -16
- package/dist/io/xlsx/rich-text.js +0 -12
- package/dist/io/xlsx/shared-formulas.js +0 -7
- package/dist/io/xlsx/shared-strings.js +0 -21
- package/dist/io/xlsx/sheet-properties.js +1 -64
- package/dist/io/xlsx/styles.d.ts +1 -29
- package/dist/io/xlsx/styles.js +2 -366
- package/dist/io/xlsx/tables.js +5 -52
- package/dist/io/xlsx/threaded-comments.js +0 -103
- package/dist/io/xlsx/workbook-xml.js +0 -94
- package/dist/io/xlsx/worksheet-xml.js +0 -174
- package/dist/io/xlsx/write-stream.js +0 -137
- package/dist/io/xlsx/write.js +1 -134
- package/dist/io/xlsx/x14-ext.js +0 -8
- package/dist/vba/cfb-writer.js +12 -59
- package/dist/vba/cfb.js +2 -32
- package/dist/vba/codepage.js +1 -23
- package/dist/vba/errors.js +0 -15
- package/dist/vba/index.js +0 -10
- package/dist/vba/ms-ovba.js +3 -46
- package/dist/vba/project-editor.js +4 -89
- package/dist/vba/project.js +2 -34
- package/dist/vba/vba-encoding.js +1 -17
- package/dist/xml/errors.js +0 -10
- package/dist/xml/xml-read.js +3 -82
- package/dist/xml/xml.js +0 -39
- package/package.json +8 -9
- package/skills/ts-xlsx-upstream/SKILL.md +178 -0
- /package/dist/io/xlsx/{pivot-read.d.ts → read-pivot.d.ts} +0 -0
- /package/dist/io/xlsx/{shared-strings-read.d.ts → read-shared-strings.d.ts} +0 -0
package/dist/core/theme.js
CHANGED
|
@@ -1,24 +1,4 @@
|
|
|
1
|
-
// The workbook theme's colour scheme — the twelve colours a `theme="n"` reference resolves against.
|
|
2
|
-
//
|
|
3
|
-
// The theme part itself is carried opaquely by the model (see `Workbook.restoreThemePart`); this
|
|
4
|
-
// module reads just the `<a:clrScheme>` out of it, because that is the only piece a colour reference
|
|
5
|
-
// needs. Everything else in a theme (the format scheme's gradients, line and effect styles) is
|
|
6
|
-
// nobody's business here.
|
|
7
1
|
import { AuthoringError } from '../errors.js';
|
|
8
|
-
/**
|
|
9
|
-
* The twelve colour-scheme slots **in the order a `theme="n"` attribute indexes them**.
|
|
10
|
-
*
|
|
11
|
-
* This order is not the order the slots appear in the theme part. ISO/IEC 29500 §20.1.6.2 documents
|
|
12
|
-
* the `<a:clrScheme>` child sequence as `dk1, lt1, dk2, lt2, accent1…6, hlink, folHlink`, and that is
|
|
13
|
-
* how the XML is written — but SpreadsheetML's `theme="n"` does **not** index that sequence. Excel
|
|
14
|
-
* swaps each dark/light pair: index 0 is `lt1`, 1 is `dk1`, 2 is `lt2`, 3 is `dk2`.
|
|
15
|
-
*
|
|
16
|
-
* Verified against Excel Desktop rather than inferred, because the two orders differ only in the
|
|
17
|
-
* first four entries and reading either one into the other silently inverts text against background
|
|
18
|
-
* — see `docs/knowledge/specs/theme-color-index-order.md` and the recorded observation in
|
|
19
|
-
* `test/corpus/fixtures/excel-oracle/theme-color-index-order.json`. The stylesheet's own default font
|
|
20
|
-
* is the everyday witness: it carries `<color theme="1"/>` and renders black, which is `dk1`.
|
|
21
|
-
*/
|
|
22
2
|
export const THEME_COLOR_SLOTS = [
|
|
23
3
|
'lt1',
|
|
24
4
|
'dk1',
|
|
@@ -33,7 +13,6 @@ export const THEME_COLOR_SLOTS = [
|
|
|
33
13
|
'hlink',
|
|
34
14
|
'folHlink',
|
|
35
15
|
];
|
|
36
|
-
/** The Office default colour scheme, matching the theme part the writer emits for a workbook with none. */
|
|
37
16
|
export const DEFAULT_THEME_COLOR_SCHEME = {
|
|
38
17
|
lt1: 'FFFFFF',
|
|
39
18
|
dk1: '000000',
|
|
@@ -48,21 +27,7 @@ export const DEFAULT_THEME_COLOR_SCHEME = {
|
|
|
48
27
|
hlink: '0563C1',
|
|
49
28
|
folHlink: '954F72',
|
|
50
29
|
};
|
|
51
|
-
// One `<a:slot>` of a `<a:clrScheme>` and the colour element inside it. Two colour models appear in
|
|
52
|
-
// practice: `<a:srgbClr val="RRGGBB"/>` states the colour directly, while `<a:sysClr val="windowText"
|
|
53
|
-
// lastClr="000000"/>` defers to an operating-system colour and records what it last resolved to.
|
|
54
|
-
// `dk1`/`lt1` are almost always the sysClr form, so a reader that only understands srgbClr resolves
|
|
55
|
-
// nothing for the two most-referenced slots in any workbook.
|
|
56
30
|
const SCHEME_SLOT = new RegExp('<a:(dk1|lt1|dk2|lt2|accent[1-6]|hlink|folHlink)\\b[^>]*>' + '\\s*<a:(srgbClr|sysClr)\\b([^>]*)>', 'g');
|
|
57
|
-
/**
|
|
58
|
-
* Extract the colour scheme from a theme part. Returns only the slots the part actually declares in a
|
|
59
|
-
* colour model this reader understands; an unrecognised one is dropped rather than guessed at, so a
|
|
60
|
-
* caller can tell "the theme says nothing here" from "the theme says black".
|
|
61
|
-
*
|
|
62
|
-
* Reads the `<a:clrScheme>` block alone. A theme carries a font scheme and a format scheme too, but
|
|
63
|
-
* neither participates in resolving a colour, and scanning the whole part would let a `<a:srgbClr>`
|
|
64
|
-
* buried in a gradient stop masquerade as a scheme slot.
|
|
65
|
-
*/
|
|
66
31
|
export function parseThemeColorScheme(themeXml) {
|
|
67
32
|
const block = /<a:clrScheme\b[^>]*>([\s\S]*?)<\/a:clrScheme>/.exec(themeXml);
|
|
68
33
|
if (block === null)
|
|
@@ -71,9 +36,6 @@ export function parseThemeColorScheme(themeXml) {
|
|
|
71
36
|
for (const match of (block[1] ?? '').matchAll(SCHEME_SLOT)) {
|
|
72
37
|
const slot = match[1];
|
|
73
38
|
const attrs = match[3] ?? '';
|
|
74
|
-
// A sysClr's `val` is a system-colour name ("windowText"), not a colour — its `lastClr` is the
|
|
75
|
-
// concrete value the authoring application last resolved that name to, and is the only thing here
|
|
76
|
-
// a consumer without the same OS theme can use.
|
|
77
39
|
const source = match[2] === 'sysClr' ? /\blastClr="([^"]*)"/ : /\bval="([^"]*)"/;
|
|
78
40
|
const value = source.exec(attrs)?.[1];
|
|
79
41
|
if (value !== undefined && /^[0-9a-fA-F]{6}$/.test(value))
|
|
@@ -81,19 +43,11 @@ export function parseThemeColorScheme(themeXml) {
|
|
|
81
43
|
}
|
|
82
44
|
return scheme;
|
|
83
45
|
}
|
|
84
|
-
/**
|
|
85
|
-
* The body typeface a workbook falls back to when neither its theme nor its styles part names one —
|
|
86
|
-
* the face the default theme nominates, and so the face every `scheme="minor"` font resolves to.
|
|
87
|
-
* Named rather than inlined because it is also the last resort of the default-font chain
|
|
88
|
-
* ({@link Workbook.defaultFont}), and the two must not drift.
|
|
89
|
-
*/
|
|
90
46
|
export const OFFICE_BODY_FACE = 'Calibri';
|
|
91
|
-
/** The Office default typefaces, matching the theme part the writer emits for a workbook with none. */
|
|
92
47
|
export const DEFAULT_THEME_FONTS = {
|
|
93
48
|
major: 'Calibri Light',
|
|
94
49
|
minor: OFFICE_BODY_FACE,
|
|
95
50
|
};
|
|
96
|
-
/** Extract the major/minor latin typefaces from a theme part's `<a:fontScheme>`. */
|
|
97
51
|
export function parseThemeFontScheme(themeXml) {
|
|
98
52
|
const block = /<a:fontScheme\b[^>]*>([\s\S]*?)<\/a:fontScheme>/.exec(themeXml);
|
|
99
53
|
if (block === null)
|
|
@@ -111,9 +65,6 @@ export function parseThemeFontScheme(themeXml) {
|
|
|
111
65
|
scheme.minor = minor;
|
|
112
66
|
return scheme;
|
|
113
67
|
}
|
|
114
|
-
// The `<a:clrScheme>` child order — dk1, lt1, dk2, lt2, accent1..6, hlink, folHlink. Not the order
|
|
115
|
-
// `theme="n"` indexes (see THEME_COLOR_SLOTS); this is the sequence CT_ColorScheme requires the
|
|
116
|
-
// elements to be written in, and writing them in index order would be schema-invalid.
|
|
117
68
|
const SCHEME_ELEMENT_ORDER = [
|
|
118
69
|
'dk1',
|
|
119
70
|
'lt1',
|
|
@@ -128,20 +79,6 @@ const SCHEME_ELEMENT_ORDER = [
|
|
|
128
79
|
'hlink',
|
|
129
80
|
'folHlink',
|
|
130
81
|
];
|
|
131
|
-
/**
|
|
132
|
-
* Apply authored colour/font overrides to a theme part, returning the new part text.
|
|
133
|
-
*
|
|
134
|
-
* Surgical by design: the base part rides through untouched except for the `<a:clrScheme>` and
|
|
135
|
-
* `<a:fontScheme>` blocks, and within those, only what the caller actually named. The format scheme —
|
|
136
|
-
* the gradients, line styles and effect styles that make a theme look like a theme — is left exactly
|
|
137
|
-
* as it was, because nobody hand-authors `fillStyleLst` gradient stops from a spreadsheet API and
|
|
138
|
-
* regenerating it would replace a designer's work with the Office default.
|
|
139
|
-
*
|
|
140
|
-
* A slot the caller did not override keeps its **verbatim source element**, not a re-serialisation of
|
|
141
|
-
* its value. That matters for `dk1`/`lt1`, which Excel writes as `<a:sysClr val="windowText"
|
|
142
|
-
* lastClr="000000"/>`: rewriting those as `<a:srgbClr>` would pin them to one machine's resolved
|
|
143
|
-
* window colours and break dark-mode following.
|
|
144
|
-
*/
|
|
145
82
|
export function applyThemeOverrides(baseXml, overrides) {
|
|
146
83
|
let xml = baseXml;
|
|
147
84
|
const colors = overrides.colors ?? {};
|
|
@@ -164,11 +101,6 @@ export function applyThemeOverrides(baseXml, overrides) {
|
|
|
164
101
|
xml = replaceLatinTypeface(xml, 'minorFont', minor);
|
|
165
102
|
return xml;
|
|
166
103
|
}
|
|
167
|
-
/**
|
|
168
|
-
* Each colour slot's verbatim inner element from a theme part — `<a:srgbClr val="…"/>` or
|
|
169
|
-
* `<a:sysClr val="…" lastClr="…"/>`. The value-level counterpart is {@link parseThemeColorScheme};
|
|
170
|
-
* this keeps the *encoding* so an untouched slot can be re-emitted exactly as the source wrote it.
|
|
171
|
-
*/
|
|
172
104
|
export function parseThemeColorElements(themeXml) {
|
|
173
105
|
const block = /<a:clrScheme\b[^>]*>([\s\S]*?)<\/a:clrScheme>/.exec(themeXml);
|
|
174
106
|
if (block === null)
|
|
@@ -183,10 +115,6 @@ export function parseThemeColorElements(themeXml) {
|
|
|
183
115
|
}
|
|
184
116
|
return elements;
|
|
185
117
|
}
|
|
186
|
-
// A theme colour is a bare 6-hex RGB — DrawingML has no alpha channel on `<a:srgbClr val>`. The two
|
|
187
|
-
// conveniences the rest of the library accepts (a leading '#', an 8-hex ARGB) are accepted and
|
|
188
|
-
// reduced here; anything else is a caller's bug and is refused rather than written as corrupt XML,
|
|
189
|
-
// which Excel does not report — it renders the slot as flat black.
|
|
190
118
|
function normalizeSchemeValue(value) {
|
|
191
119
|
const hex = value.startsWith('#') ? value.slice(1) : value;
|
|
192
120
|
const rgb = hex.length === 8 ? hex.slice(2) : hex;
|
|
@@ -195,15 +123,10 @@ function normalizeSchemeValue(value) {
|
|
|
195
123
|
}
|
|
196
124
|
return rgb.toUpperCase();
|
|
197
125
|
}
|
|
198
|
-
// Replace the body of `<a:name>…</a:name>`, keeping the element's own attributes (the scheme's
|
|
199
|
-
// display name). A base with no such block is left alone: this authors an existing theme, and a theme
|
|
200
|
-
// that declares no colour scheme at all is not one an override can repair.
|
|
201
126
|
function replaceBlockBody(xml, name, body) {
|
|
202
127
|
const pattern = new RegExp(`(<a:${name}\\b[^>]*>)[\\s\\S]*?(</a:${name}>)`);
|
|
203
128
|
return xml.replace(pattern, (_all, open, close) => `${open}${body}${close}`);
|
|
204
129
|
}
|
|
205
|
-
// Swap just the `<a:latin typeface="…"/>` inside one of the two font slots, leaving its `panose` and
|
|
206
|
-
// the east-asian/complex-script faces beside it as they were.
|
|
207
130
|
function replaceLatinTypeface(xml, which, typeface) {
|
|
208
131
|
const pattern = new RegExp(`(<a:${which}\\b[^>]*>[\\s\\S]*?<a:latin\\b)[^>]*(/>)`);
|
|
209
132
|
return xml.replace(pattern, (_all, open, close) => `${open} typeface="${escapeXmlAttr(typeface)}"${close}`);
|
|
@@ -215,14 +138,6 @@ function escapeXmlAttr(value) {
|
|
|
215
138
|
.replace(/>/g, '>')
|
|
216
139
|
.replace(/"/g, '"');
|
|
217
140
|
}
|
|
218
|
-
/**
|
|
219
|
-
* The theme part a workbook with no theme of its own ships — the standard Office theme.
|
|
220
|
-
*
|
|
221
|
-
* A spreadsheet must carry one even when nobody configured it: the stylesheet's own default font
|
|
222
|
-
* references `theme="1"`, which a consumer can only resolve against this part, so the two travel
|
|
223
|
-
* together. It is also the base {@link applyThemeOverrides} authors on top of when a workbook was
|
|
224
|
-
* built from scratch rather than read from a file.
|
|
225
|
-
*/
|
|
226
141
|
export const DEFAULT_THEME_XML = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\n' +
|
|
227
142
|
'<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Office Theme">' +
|
|
228
143
|
'<a:themeElements>' +
|
package/dist/core/value.js
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
// The cell value model — the second bedrock primitive after addressing.
|
|
2
|
-
//
|
|
3
|
-
// A cell holds exactly one value, and that value's *type* is observable and drives
|
|
4
|
-
// everything downstream (serialization, number-format application, formula results).
|
|
5
|
-
// The honest shape here is a discriminated union: a value is either a JS primitive
|
|
6
|
-
// (null / number / string / boolean / Date) or one of the structural OOXML value
|
|
7
|
-
// shapes (error, formula, shared formula, rich text, hyperlink). There is no
|
|
8
|
-
// stringly-typed sentinel and no silent coercion between kinds — a numeric-looking
|
|
9
|
-
// string stays a string, because the caller's chosen type is the source of truth.
|
|
10
|
-
/** The observable kind of a cell's value. Both formula shapes report as `Formula`. */
|
|
11
1
|
export const ValueType = {
|
|
12
2
|
Null: 'null',
|
|
13
3
|
Number: 'number',
|
|
@@ -19,7 +9,6 @@ export const ValueType = {
|
|
|
19
9
|
RichText: 'richText',
|
|
20
10
|
Hyperlink: 'hyperlink',
|
|
21
11
|
};
|
|
22
|
-
/** The canonical Excel error literals a cell (or formula result) can carry. */
|
|
23
12
|
export const ERROR_CODES = [
|
|
24
13
|
'#N/A',
|
|
25
14
|
'#REF!',
|
|
@@ -33,78 +22,30 @@ export const ERROR_CODES = [
|
|
|
33
22
|
'#GETTING_DATA',
|
|
34
23
|
];
|
|
35
24
|
const ERROR_SET = new Set(ERROR_CODES);
|
|
36
|
-
// Whether a value is a non-null object carrying `key` — the object-shaped {@link CellValue} kinds are all
|
|
37
|
-
// discriminated by the presence of a single property, so every guard below narrows through this one test.
|
|
38
25
|
function hasKey(value, key) {
|
|
39
26
|
return typeof value === 'object' && value !== null && key in value;
|
|
40
27
|
}
|
|
41
|
-
/**
|
|
42
|
-
* Whether a value is an in-cell error ({@link ErrorValue}). The narrowing counterpart of
|
|
43
|
-
* `detectValueType(value) === ValueType.Error`: use this one when the branch goes on to read
|
|
44
|
-
* `.error`, and {@link detectValueType} when it dispatches over all nine kinds at once.
|
|
45
|
-
*/
|
|
46
28
|
export function isErrorValue(value) {
|
|
47
29
|
return hasKey(value, 'error');
|
|
48
30
|
}
|
|
49
|
-
/**
|
|
50
|
-
* Whether a value is a cell's own formula ({@link FormulaValue}) — a master, or a formula
|
|
51
|
-
* belonging to no shared group. A shared-formula clone is **not** one of these; see
|
|
52
|
-
* {@link isSharedFormulaValue}. Both report as `ValueType.Formula`, so a caller that means "any
|
|
53
|
-
* formula-shaped cell" wants {@link detectValueType}, not this.
|
|
54
|
-
*/
|
|
55
31
|
export function isFormulaValue(value) {
|
|
56
|
-
// A shared-formula clone resolved on read carries both its master address (`sharedFormula`) and the
|
|
57
|
-
// translated `formula`; it is a SharedFormulaValue, so exclude it here to keep the two kinds distinct.
|
|
58
32
|
return hasKey(value, 'formula') && !('sharedFormula' in value);
|
|
59
33
|
}
|
|
60
|
-
/** Whether a value is a clone participating in a shared formula ({@link SharedFormulaValue}). */
|
|
61
34
|
export function isSharedFormulaValue(value) {
|
|
62
35
|
return hasKey(value, 'sharedFormula');
|
|
63
36
|
}
|
|
64
|
-
/** Whether a value is a What-If-Analysis data-table formula ({@link DataTableFormulaValue}). */
|
|
65
37
|
export function isDataTableFormulaValue(value) {
|
|
66
38
|
return hasKey(value, 'shareType') && value.shareType === 'dataTable';
|
|
67
39
|
}
|
|
68
|
-
/**
|
|
69
|
-
* Whether a value is composed of formatted runs ({@link RichTextValue}). This is the test to
|
|
70
|
-
* make before {@link richTextToPlain}, which accepts nothing else.
|
|
71
|
-
*/
|
|
72
40
|
export function isRichTextValue(value) {
|
|
73
41
|
return hasKey(value, 'richText');
|
|
74
42
|
}
|
|
75
|
-
/**
|
|
76
|
-
* Whether a value is a hyperlink ({@link HyperlinkValue}). Note that its `text` is itself either
|
|
77
|
-
* a string or a {@link RichTextValue}, so reading the label out means one more narrowing.
|
|
78
|
-
*/
|
|
79
43
|
export function isHyperlinkValue(value) {
|
|
80
44
|
return hasKey(value, 'hyperlink');
|
|
81
45
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Flatten a rich-text value to its plain text by concatenating every run's text in order. This is the
|
|
84
|
-
* text a consumer that cannot render per-run formatting (a CSV field, a pivot cache entry) sees, and
|
|
85
|
-
* the string a rich cell reads as when its formatting is discarded.
|
|
86
|
-
*/
|
|
87
46
|
export function richTextToPlain(value) {
|
|
88
47
|
return value.richText.map((run) => run.text).join('');
|
|
89
48
|
}
|
|
90
|
-
/**
|
|
91
|
-
* The plain text of any cell value — total over {@link CellValue}, so a caller reading a sheet
|
|
92
|
-
* whose cells it did not write never has to switch on the union itself.
|
|
93
|
-
*
|
|
94
|
-
* This is the *value's* text, not the cell's *display* text: a number renders as JavaScript
|
|
95
|
-
* renders it, with no number format applied (`0.1 + 0.2` is `"0.30000000000000004"`, a currency
|
|
96
|
-
* cell has no currency sign), because the format lives on the style and this function is given
|
|
97
|
-
* only the value. What each kind yields:
|
|
98
|
-
*
|
|
99
|
-
* - the empty cell (`null`) and an invalid `Date` → `""`, the two ways a cell has no text
|
|
100
|
-
* - a boolean → `"TRUE"` / `"FALSE"`, Excel's own literals rather than JavaScript's
|
|
101
|
-
* - a `Date` → a full ISO-8601 timestamp
|
|
102
|
-
* - an error → its literal, e.g. `"#REF!"` — the same string the grid shows
|
|
103
|
-
* - rich text → every run concatenated ({@link richTextToPlain})
|
|
104
|
-
* - a hyperlink → its label, never its destination
|
|
105
|
-
* - any of the three formula kinds → the text of the *cached result*, and `""` when the cell
|
|
106
|
-
* carries no cached result: the formula source is not text the sheet ever displayed
|
|
107
|
-
*/
|
|
108
49
|
export function cellValueToText(value) {
|
|
109
50
|
if (value === null)
|
|
110
51
|
return '';
|
|
@@ -120,12 +61,9 @@ export function cellValueToText(value) {
|
|
|
120
61
|
}
|
|
121
62
|
if (value instanceof Date)
|
|
122
63
|
return Number.isNaN(value.getTime()) ? '' : value.toISOString();
|
|
123
|
-
// Same precedence as detectValueType, and for the same reason: the outer shape wins, so a
|
|
124
|
-
// hyperlink whose label is rich text renders as a hyperlink's label, not as rich text.
|
|
125
64
|
if (isHyperlinkValue(value)) {
|
|
126
65
|
return typeof value.text === 'string' ? value.text : richTextToPlain(value.text);
|
|
127
66
|
}
|
|
128
|
-
// Every formula kind carries a `result` of the same optional shape; one recursion renders it.
|
|
129
67
|
if (isFormulaValue(value) || isSharedFormulaValue(value) || isDataTableFormulaValue(value)) {
|
|
130
68
|
return value.result === undefined ? '' : cellValueToText(value.result);
|
|
131
69
|
}
|
|
@@ -135,12 +73,6 @@ export function cellValueToText(value) {
|
|
|
135
73
|
return value.error;
|
|
136
74
|
return unsupportedValue(value);
|
|
137
75
|
}
|
|
138
|
-
/**
|
|
139
|
-
* Classify a value into its observable {@link ValueType}. This is total over
|
|
140
|
-
* {@link CellValue}: every legal value has exactly one type. A `Date` is a date even
|
|
141
|
-
* when its time is `NaN` (an invalid date is still a date-typed cell); serialization,
|
|
142
|
-
* not the model, decides what to do with it.
|
|
143
|
-
*/
|
|
144
76
|
export function detectValueType(value) {
|
|
145
77
|
if (value === null)
|
|
146
78
|
return ValueType.Null;
|
|
@@ -156,8 +88,6 @@ export function detectValueType(value) {
|
|
|
156
88
|
}
|
|
157
89
|
if (value instanceof Date)
|
|
158
90
|
return ValueType.Date;
|
|
159
|
-
// Order matters: a hyperlink whose text is rich must classify as Hyperlink, and a
|
|
160
|
-
// formula carrying a result must classify as Formula — check the outer shape first.
|
|
161
91
|
if (isHyperlinkValue(value))
|
|
162
92
|
return ValueType.Hyperlink;
|
|
163
93
|
if (isFormulaValue(value) || isSharedFormulaValue(value) || isDataTableFormulaValue(value)) {
|
|
@@ -169,14 +99,9 @@ export function detectValueType(value) {
|
|
|
169
99
|
return ValueType.Error;
|
|
170
100
|
return unsupportedValue(value);
|
|
171
101
|
}
|
|
172
|
-
/**
|
|
173
|
-
* The verdict "this is not a cell value at all", raised from one place so that every function
|
|
174
|
-
* total over {@link CellValue} rejects the same inputs with the same message.
|
|
175
|
-
*/
|
|
176
102
|
function unsupportedValue(value) {
|
|
177
103
|
throw new TypeError(`unsupported cell value: ${describe(value)}`);
|
|
178
104
|
}
|
|
179
|
-
/** Whether a string is one of Excel's canonical error literals. */
|
|
180
105
|
export function isErrorCode(text) {
|
|
181
106
|
return ERROR_SET.has(text);
|
|
182
107
|
}
|
|
@@ -186,35 +111,16 @@ function describe(value) {
|
|
|
186
111
|
}
|
|
187
112
|
return String(value);
|
|
188
113
|
}
|
|
189
|
-
/**
|
|
190
|
-
* Normalise a raw assignment into a stored {@link CellValue}. `undefined` becomes the
|
|
191
|
-
* empty cell (`null`); every other kind is validated by {@link detectValueType}. The
|
|
192
|
-
* model never rewrites one value *kind* into another (a numeric-looking string stays a
|
|
193
|
-
* string) — the single exception is formula text, which is canonicalised to the OOXML
|
|
194
|
-
* stored form (no leading `=`) so round-trips are idempotent regardless of how the
|
|
195
|
-
* caller supplied it.
|
|
196
|
-
*
|
|
197
|
-
* @throws {TypeError} if the value is not a recognised cell-value shape.
|
|
198
|
-
*/
|
|
199
114
|
export function coerceCellValue(value) {
|
|
200
115
|
if (value === undefined)
|
|
201
116
|
return null;
|
|
202
|
-
// detectValueType throws on an unrecognised object shape, so this both validates
|
|
203
|
-
// and gives a precise error at the assignment site rather than deep in serialization.
|
|
204
117
|
detectValueType(value);
|
|
205
118
|
if (isFormulaValue(value)) {
|
|
206
119
|
const formula = stripLeadingEquals(value.formula);
|
|
207
120
|
return formula === value.formula ? value : { ...value, formula };
|
|
208
121
|
}
|
|
209
|
-
// A shared formula's `sharedFormula` is a master cell address, not formula text, so there is no
|
|
210
|
-
// leading `=` to canonicalise — it passes through as given.
|
|
211
122
|
return value;
|
|
212
123
|
}
|
|
213
|
-
/**
|
|
214
|
-
* OOXML stores a formula's text in `<f>` without the UI's leading `=`. Strip a single
|
|
215
|
-
* leading `=` so the stored form is canonical and a strict consumer (Google Sheets,
|
|
216
|
-
* WPS) — which rejects a stored formula beginning with `=` — accepts the file.
|
|
217
|
-
*/
|
|
218
124
|
function stripLeadingEquals(formula) {
|
|
219
125
|
return formula.startsWith('=') ? formula.slice(1) : formula;
|
|
220
126
|
}
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
// Workbook-level protection: the `<workbookProtection>` element in CT_Workbook that locks the
|
|
2
|
-
// workbook's *structure* (sheets cannot be added, deleted, reordered, or unhidden) and/or its
|
|
3
|
-
// *windows*. It is distinct from worksheet-level `<sheetProtection>` — that guards a single sheet's
|
|
4
|
-
// cells; this guards the workbook shell. A weak integrity signal, but a real one: dropping it on a
|
|
5
|
-
// passthrough save silently unlocks a file the author locked, so the model preserves it faithfully.
|
|
6
|
-
/**
|
|
7
|
-
* The password/agile-hash attributes a `<workbookProtection>` element may carry, preserved verbatim
|
|
8
|
-
* so a hash-guarded workbook round-trips without the library ever interpreting the credential. Both
|
|
9
|
-
* the legacy 16-bit `*Password` hashes and the modern agile `*AlgorithmName`/`*HashValue`/
|
|
10
|
-
* `*SaltValue`/`*SpinCount` quartets appear here, for the structure guard (`workbook*`) and the
|
|
11
|
-
* revisions guard (`revisions*`). The reader accepts only these names, so a hostile or unknown
|
|
12
|
-
* attribute is never echoed back into the output.
|
|
13
|
-
*/
|
|
14
1
|
export const WORKBOOK_PROTECTION_CREDENTIAL_ATTRS = [
|
|
15
2
|
'workbookPassword',
|
|
16
3
|
'workbookAlgorithmName',
|