@shbernal/ts-xlsx 1.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/LICENSE +23 -0
- package/README.md +230 -0
- package/dist/core/address.d.ts +49 -0
- package/dist/core/address.js +125 -0
- package/dist/core/autofilter.d.ts +55 -0
- package/dist/core/autofilter.js +48 -0
- package/dist/core/cell.d.ts +132 -0
- package/dist/core/cell.js +249 -0
- package/dist/core/color-resolution.d.ts +58 -0
- package/dist/core/color-resolution.js +170 -0
- package/dist/core/column.d.ts +87 -0
- package/dist/core/column.js +167 -0
- package/dist/core/comment-thread.d.ts +116 -0
- package/dist/core/comment-thread.js +62 -0
- package/dist/core/conditional-formatting-overlay.d.ts +15 -0
- package/dist/core/conditional-formatting-overlay.js +24 -0
- package/dist/core/conditional-formatting.d.ts +70 -0
- package/dist/core/conditional-formatting.js +35 -0
- package/dist/core/containers.d.ts +2 -0
- package/dist/core/containers.js +25 -0
- package/dist/core/data-validation-overlay.d.ts +28 -0
- package/dist/core/data-validation-overlay.js +56 -0
- package/dist/core/data-validation.d.ts +38 -0
- package/dist/core/data-validation.js +13 -0
- package/dist/core/date.d.ts +25 -0
- package/dist/core/date.js +58 -0
- package/dist/core/formula.d.ts +49 -0
- package/dist/core/formula.js +319 -0
- package/dist/core/grid-edits.d.ts +22 -0
- package/dist/core/grid-edits.js +206 -0
- package/dist/core/image.d.ts +76 -0
- package/dist/core/image.js +65 -0
- package/dist/core/internal.d.ts +11 -0
- package/dist/core/internal.js +29 -0
- package/dist/core/merge.d.ts +13 -0
- package/dist/core/merge.js +26 -0
- package/dist/core/modern-functions.d.ts +1 -0
- package/dist/core/modern-functions.js +165 -0
- package/dist/core/page-setup.d.ts +94 -0
- package/dist/core/page-setup.js +6 -0
- package/dist/core/pivot-table.d.ts +113 -0
- package/dist/core/pivot-table.js +249 -0
- package/dist/core/preserved.d.ts +58 -0
- package/dist/core/preserved.js +5 -0
- package/dist/core/protection.d.ts +67 -0
- package/dist/core/protection.js +68 -0
- package/dist/core/range.d.ts +112 -0
- package/dist/core/range.js +283 -0
- package/dist/core/row.d.ts +73 -0
- package/dist/core/row.js +135 -0
- package/dist/core/style.d.ts +246 -0
- package/dist/core/style.js +125 -0
- package/dist/core/table-style.d.ts +67 -0
- package/dist/core/table-style.js +90 -0
- package/dist/core/table.d.ts +194 -0
- package/dist/core/table.js +291 -0
- package/dist/core/theme.d.ts +90 -0
- package/dist/core/theme.js +272 -0
- package/dist/core/value.d.ts +115 -0
- package/dist/core/value.js +144 -0
- package/dist/core/workbook-protection.d.ts +27 -0
- package/dist/core/workbook-protection.js +25 -0
- package/dist/core/workbook.d.ts +528 -0
- package/dist/core/workbook.js +732 -0
- package/dist/core/worksheet-model.d.ts +34 -0
- package/dist/core/worksheet-model.js +100 -0
- package/dist/core/worksheet.d.ts +647 -0
- package/dist/core/worksheet.js +1025 -0
- package/dist/customui/errors.d.ts +15 -0
- package/dist/customui/errors.js +15 -0
- package/dist/customui/index.d.ts +2 -0
- package/dist/customui/index.js +7 -0
- package/dist/customui/ribbon.d.ts +86 -0
- package/dist/customui/ribbon.js +180 -0
- package/dist/entries/core.d.ts +23 -0
- package/dist/entries/core.js +20 -0
- package/dist/entries/csv.d.ts +2 -0
- package/dist/entries/csv.js +6 -0
- package/dist/entries/customui.d.ts +1 -0
- package/dist/entries/customui.js +3 -0
- package/dist/entries/errors.d.ts +7 -0
- package/dist/entries/errors.js +20 -0
- package/dist/entries/vba.d.ts +2 -0
- package/dist/entries/vba.js +7 -0
- package/dist/entries/xlsb.d.ts +1 -0
- package/dist/entries/xlsb.js +7 -0
- package/dist/entries/xlsx.d.ts +5 -0
- package/dist/entries/xlsx.js +18 -0
- package/dist/errors.d.ts +65 -0
- package/dist/errors.js +66 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +19 -0
- package/dist/io/csv/read.d.ts +15 -0
- package/dist/io/csv/read.js +113 -0
- package/dist/io/csv/write.d.ts +28 -0
- package/dist/io/csv/write.js +124 -0
- package/dist/io/opc/errors.d.ts +46 -0
- package/dist/io/opc/errors.js +49 -0
- package/dist/io/opc/inflate.d.ts +15 -0
- package/dist/io/opc/inflate.js +91 -0
- package/dist/io/opc/namespaces.d.ts +18 -0
- package/dist/io/opc/namespaces.js +18 -0
- package/dist/io/opc/part-paths.d.ts +4 -0
- package/dist/io/opc/part-paths.js +35 -0
- package/dist/io/opc/read-opc.d.ts +24 -0
- package/dist/io/opc/read-opc.js +153 -0
- package/dist/io/opc/read-options.d.ts +10 -0
- package/dist/io/opc/read-options.js +7 -0
- package/dist/io/opc/rels.d.ts +15 -0
- package/dist/io/opc/rels.js +28 -0
- package/dist/io/opc/sniff-format.d.ts +27 -0
- package/dist/io/opc/sniff-format.js +92 -0
- package/dist/io/style/xf-style.d.ts +47 -0
- package/dist/io/style/xf-style.js +95 -0
- package/dist/io/xlsb/errors.d.ts +15 -0
- package/dist/io/xlsb/errors.js +15 -0
- package/dist/io/xlsb/formula.d.ts +48 -0
- package/dist/io/xlsb/formula.js +398 -0
- package/dist/io/xlsb/primitives.d.ts +86 -0
- package/dist/io/xlsb/primitives.js +230 -0
- package/dist/io/xlsb/ptg-functions.d.ts +16 -0
- package/dist/io/xlsb/ptg-functions.js +659 -0
- package/dist/io/xlsb/read-shared-strings.d.ts +2 -0
- package/dist/io/xlsb/read-shared-strings.js +22 -0
- package/dist/io/xlsb/read-styles.d.ts +4 -0
- package/dist/io/xlsb/read-styles.js +386 -0
- package/dist/io/xlsb/read-worksheet.d.ts +9 -0
- package/dist/io/xlsb/read-worksheet.js +301 -0
- package/dist/io/xlsb/read.d.ts +20 -0
- package/dist/io/xlsb/read.js +199 -0
- package/dist/io/xlsb/record-stream.d.ts +14 -0
- package/dist/io/xlsb/record-stream.js +56 -0
- package/dist/io/xlsb/record-types.d.ts +55 -0
- package/dist/io/xlsb/record-types.js +73 -0
- package/dist/io/xlsx/cell-accumulator.d.ts +25 -0
- package/dist/io/xlsx/cell-accumulator.js +172 -0
- package/dist/io/xlsx/cell-value.d.ts +32 -0
- package/dist/io/xlsx/cell-value.js +85 -0
- package/dist/io/xlsx/comments.d.ts +74 -0
- package/dist/io/xlsx/comments.js +243 -0
- package/dist/io/xlsx/conditional-formatting.d.ts +32 -0
- package/dist/io/xlsx/conditional-formatting.js +464 -0
- package/dist/io/xlsx/data-validation.d.ts +20 -0
- package/dist/io/xlsx/data-validation.js +258 -0
- package/dist/io/xlsx/edit-vba.d.ts +23 -0
- package/dist/io/xlsx/edit-vba.js +139 -0
- package/dist/io/xlsx/errors.d.ts +17 -0
- package/dist/io/xlsx/errors.js +17 -0
- package/dist/io/xlsx/hyperlinks.d.ts +42 -0
- package/dist/io/xlsx/hyperlinks.js +139 -0
- package/dist/io/xlsx/images.d.ts +34 -0
- package/dist/io/xlsx/images.js +218 -0
- package/dist/io/xlsx/namespaces.d.ts +55 -0
- package/dist/io/xlsx/namespaces.js +55 -0
- package/dist/io/xlsx/package-plan.d.ts +103 -0
- package/dist/io/xlsx/package-plan.js +168 -0
- package/dist/io/xlsx/pivot-read.d.ts +5 -0
- package/dist/io/xlsx/pivot-read.js +134 -0
- package/dist/io/xlsx/pivot.d.ts +12 -0
- package/dist/io/xlsx/pivot.js +141 -0
- package/dist/io/xlsx/read-rows.d.ts +85 -0
- package/dist/io/xlsx/read-rows.js +286 -0
- package/dist/io/xlsx/read-styles.d.ts +8 -0
- package/dist/io/xlsx/read-styles.js +551 -0
- package/dist/io/xlsx/read-worksheet.d.ts +4 -0
- package/dist/io/xlsx/read-worksheet.js +528 -0
- package/dist/io/xlsx/read.d.ts +32 -0
- package/dist/io/xlsx/read.js +680 -0
- package/dist/io/xlsx/relationships.d.ts +33 -0
- package/dist/io/xlsx/relationships.js +47 -0
- package/dist/io/xlsx/rich-runs.d.ts +13 -0
- package/dist/io/xlsx/rich-runs.js +59 -0
- package/dist/io/xlsx/rich-text.d.ts +7 -0
- package/dist/io/xlsx/rich-text.js +23 -0
- package/dist/io/xlsx/shared-formulas.d.ts +6 -0
- package/dist/io/xlsx/shared-formulas.js +52 -0
- package/dist/io/xlsx/shared-strings-read.d.ts +2 -0
- package/dist/io/xlsx/shared-strings-read.js +70 -0
- package/dist/io/xlsx/shared-strings.d.ts +17 -0
- package/dist/io/xlsx/shared-strings.js +50 -0
- package/dist/io/xlsx/sheet-properties.d.ts +13 -0
- package/dist/io/xlsx/sheet-properties.js +211 -0
- package/dist/io/xlsx/styles.d.ts +138 -0
- package/dist/io/xlsx/styles.js +821 -0
- package/dist/io/xlsx/tables.d.ts +9 -0
- package/dist/io/xlsx/tables.js +208 -0
- package/dist/io/xlsx/threaded-comments.d.ts +102 -0
- package/dist/io/xlsx/threaded-comments.js +308 -0
- package/dist/io/xlsx/workbook-xml.d.ts +12 -0
- package/dist/io/xlsx/workbook-xml.js +353 -0
- package/dist/io/xlsx/worksheet-xml.d.ts +86 -0
- package/dist/io/xlsx/worksheet-xml.js +643 -0
- package/dist/io/xlsx/write-stream.d.ts +171 -0
- package/dist/io/xlsx/write-stream.js +399 -0
- package/dist/io/xlsx/write.d.ts +81 -0
- package/dist/io/xlsx/write.js +396 -0
- package/dist/io/xlsx/x14-ext.d.ts +9 -0
- package/dist/io/xlsx/x14-ext.js +12 -0
- package/dist/vba/cfb-writer.d.ts +19 -0
- package/dist/vba/cfb-writer.js +277 -0
- package/dist/vba/cfb.d.ts +16 -0
- package/dist/vba/cfb.js +324 -0
- package/dist/vba/codepage.d.ts +18 -0
- package/dist/vba/codepage.js +87 -0
- package/dist/vba/errors.d.ts +24 -0
- package/dist/vba/errors.js +24 -0
- package/dist/vba/index.d.ts +5 -0
- package/dist/vba/index.js +15 -0
- package/dist/vba/ms-ovba.d.ts +12 -0
- package/dist/vba/ms-ovba.js +176 -0
- package/dist/vba/project-editor.d.ts +62 -0
- package/dist/vba/project-editor.js +377 -0
- package/dist/vba/project.d.ts +45 -0
- package/dist/vba/project.js +160 -0
- package/dist/vba/vba-encoding.d.ts +14 -0
- package/dist/vba/vba-encoding.js +39 -0
- package/dist/xml/errors.d.ts +15 -0
- package/dist/xml/errors.js +15 -0
- package/dist/xml/xml-read.d.ts +107 -0
- package/dist/xml/xml-read.js +283 -0
- package/dist/xml/xml.d.ts +41 -0
- package/dist/xml/xml.js +86 -0
- package/package.json +117 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { XlsxError } from '../errors.ts';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when a `customUI` ribbon-customisation part (`customUI/customUI.xml` or
|
|
4
|
+
* `customUI/customUI14.xml`) is present but cannot be parsed into a {@link CustomUiDocument} — malformed
|
|
5
|
+
* XML, a missing or unrecognised `<customUI>` root namespace, or nesting deep enough to look hostile.
|
|
6
|
+
* A workbook with no ribbon customisation never produces this: {@link Workbook.customUI} is an empty
|
|
7
|
+
* array instead.
|
|
8
|
+
*
|
|
9
|
+
* The parser treats the part as hostile input (a spreadsheet library parses untrusted files), so a
|
|
10
|
+
* malformed structure fails closed with this error rather than yielding a half-built tree.
|
|
11
|
+
*/
|
|
12
|
+
export declare class CustomUiParseError extends XlsxError {
|
|
13
|
+
readonly name = "CustomUiParseError";
|
|
14
|
+
readonly code = "malformed-input";
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { XlsxError } from "../errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* Thrown when a `customUI` ribbon-customisation part (`customUI/customUI.xml` or
|
|
4
|
+
* `customUI/customUI14.xml`) is present but cannot be parsed into a {@link CustomUiDocument} — malformed
|
|
5
|
+
* XML, a missing or unrecognised `<customUI>` root namespace, or nesting deep enough to look hostile.
|
|
6
|
+
* A workbook with no ribbon customisation never produces this: {@link Workbook.customUI} is an empty
|
|
7
|
+
* array instead.
|
|
8
|
+
*
|
|
9
|
+
* The parser treats the part as hostile input (a spreadsheet library parses untrusted files), so a
|
|
10
|
+
* malformed structure fails closed with this error rather than yielding a half-built tree.
|
|
11
|
+
*/
|
|
12
|
+
export class CustomUiParseError extends XlsxError {
|
|
13
|
+
name = 'CustomUiParseError';
|
|
14
|
+
code = 'malformed-input';
|
|
15
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { CustomUiParseError } from './errors.ts';
|
|
2
|
+
export { CUSTOMUI_2006_NAMESPACE, CUSTOMUI_2007_REL_TYPE, CUSTOMUI_2009_NAMESPACE, CUSTOMUI_2010_REL_TYPE, type CustomUiDocument, isCustomUiRelType, parseCustomUi, type Ribbon, type RibbonControl, type RibbonControlKind, type RibbonDialect, type RibbonGroup, type RibbonTab, } from './ribbon.ts';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Native `customUI` ribbon read: project a typed, read-only view of the ribbon tree (tabs → groups →
|
|
2
|
+
// controls + callback names) over the `customUI.xml` / `customUI14.xml` parts a macro workbook carries.
|
|
3
|
+
// Those parts are preserved byte-for-byte on round-trip (see `src/core/preserved.ts`); this is a
|
|
4
|
+
// projection over them, the same posture as the VBA read view (`src/vba`). Authoring/editing the ribbon
|
|
5
|
+
// is out of scope — round-trip fidelity already comes from verbatim preservation.
|
|
6
|
+
export { CustomUiParseError } from "./errors.js";
|
|
7
|
+
export { CUSTOMUI_2006_NAMESPACE, CUSTOMUI_2007_REL_TYPE, CUSTOMUI_2009_NAMESPACE, CUSTOMUI_2010_REL_TYPE, isCustomUiRelType, parseCustomUi, } from "./ribbon.js";
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/** The `customUI` schema a part is written against — the read model keys off this, not the (frequently
|
|
2
|
+
* mis-copied) relationship type. `2007` is the original RibbonX (`customUI.xml`); `2010` is the later
|
|
3
|
+
* schema (`customUI14.xml`) that also carries backstage/QAT/commands. */
|
|
4
|
+
export type RibbonDialect = '2007' | '2010';
|
|
5
|
+
/** The `customUI` root namespaces, one per {@link RibbonDialect}. */
|
|
6
|
+
export declare const CUSTOMUI_2006_NAMESPACE = "http://schemas.microsoft.com/office/2006/01/customui";
|
|
7
|
+
export declare const CUSTOMUI_2009_NAMESPACE = "http://schemas.microsoft.com/office/2009/07/customui";
|
|
8
|
+
export declare const CUSTOMUI_2007_REL_TYPE = "http://schemas.microsoft.com/office/2006/relationships/ui/extensibility";
|
|
9
|
+
export declare const CUSTOMUI_2010_REL_TYPE = "http://schemas.microsoft.com/office/2007/relationships/ui/extensibility";
|
|
10
|
+
/** Whether a package-root relationship Type URI points at a `customUI` ribbon part. */
|
|
11
|
+
export declare function isCustomUiRelType(type: string): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* A control element inside a ribbon group. `kind` is the element's local name, narrowed to the closed
|
|
14
|
+
* set of RibbonX control elements ({@link RibbonControlKind}); an element outside that set is surfaced
|
|
15
|
+
* as `unknown` rather than dropped. The three identity attributes (`id` a document-defined control,
|
|
16
|
+
* `idQ` a qualified id, `idMso` a built-in control) and the two most-consulted display/behaviour
|
|
17
|
+
* attributes (`label`, `onAction`) are lifted out as typed conveniences; every attribute the element
|
|
18
|
+
* actually carried — including the many `get*` dynamic callbacks and layout hints not modelled here —
|
|
19
|
+
* is preserved verbatim in {@link attributes}, so nothing is lost. Container controls (a `menu`,
|
|
20
|
+
* `splitButton`, `gallery`, `dropDown`, `box`, …) carry their nested controls/items in {@link children}.
|
|
21
|
+
*/
|
|
22
|
+
export interface RibbonControl {
|
|
23
|
+
readonly kind: RibbonControlKind;
|
|
24
|
+
/** A document-defined control id. */
|
|
25
|
+
readonly id?: string;
|
|
26
|
+
/** A namespace-qualified control id (`idQ`), used to reference a control across add-ins. */
|
|
27
|
+
readonly idQ?: string;
|
|
28
|
+
/** The id of a built-in (Microsoft-defined) control this element repurposes or places against. */
|
|
29
|
+
readonly idMso?: string;
|
|
30
|
+
/** The static label, when the element carries one (a dynamic label uses `getLabel`, in {@link attributes}). */
|
|
31
|
+
readonly label?: string;
|
|
32
|
+
/** The callback procedure name invoked on activation — the macro a click runs. */
|
|
33
|
+
readonly onAction?: string;
|
|
34
|
+
/** Every attribute on the element, verbatim and entity-decoded. The typed fields above are lifted from
|
|
35
|
+
* here; this map is the complete record, including attributes this model does not lift out. */
|
|
36
|
+
readonly attributes: Readonly<Record<string, string>>;
|
|
37
|
+
/** Nested controls or items, for a container control; absent for a leaf control. */
|
|
38
|
+
readonly children?: readonly RibbonControl[];
|
|
39
|
+
}
|
|
40
|
+
/** The RibbonX control elements this reader recognises. `item` is a `dropDown`/`gallery`/`comboBox`
|
|
41
|
+
* entry; `unknown` is the fallback for any element outside this set (never silently dropped). */
|
|
42
|
+
export type RibbonControlKind = 'button' | 'toggleButton' | 'checkBox' | 'editBox' | 'dropDown' | 'comboBox' | 'gallery' | 'menu' | 'dynamicMenu' | 'splitButton' | 'buttonGroup' | 'box' | 'labelControl' | 'separator' | 'menuSeparator' | 'dialogBoxLauncher' | 'control' | 'item' | 'unknown';
|
|
43
|
+
/** A `<group>` within a ribbon tab: its identity/label attributes and the controls it contains. */
|
|
44
|
+
export interface RibbonGroup {
|
|
45
|
+
readonly id?: string;
|
|
46
|
+
readonly idQ?: string;
|
|
47
|
+
readonly idMso?: string;
|
|
48
|
+
readonly label?: string;
|
|
49
|
+
/** Every attribute on the `<group>`, verbatim. */
|
|
50
|
+
readonly attributes: Readonly<Record<string, string>>;
|
|
51
|
+
readonly controls: readonly RibbonControl[];
|
|
52
|
+
}
|
|
53
|
+
/** A `<tab>` within the ribbon: its identity/label attributes and the groups it contains. */
|
|
54
|
+
export interface RibbonTab {
|
|
55
|
+
readonly id?: string;
|
|
56
|
+
readonly idQ?: string;
|
|
57
|
+
readonly idMso?: string;
|
|
58
|
+
readonly label?: string;
|
|
59
|
+
/** Every attribute on the `<tab>`, verbatim. */
|
|
60
|
+
readonly attributes: Readonly<Record<string, string>>;
|
|
61
|
+
readonly groups: readonly RibbonGroup[];
|
|
62
|
+
}
|
|
63
|
+
/** The parsed `<ribbon>` element: whether it starts from a blank ribbon, and its custom tabs. Only the
|
|
64
|
+
* `<tabs>` subtree is modelled; `qat` and `contextualTabs` are not parsed in v1. */
|
|
65
|
+
export interface Ribbon {
|
|
66
|
+
/** `startFromScratch="true"` reduces the built-in ribbon to a minimal set before custom tabs apply. */
|
|
67
|
+
readonly startFromScratch: boolean;
|
|
68
|
+
readonly tabs: readonly RibbonTab[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A parsed `customUI` part. `dialect` records which schema it was written against (derived from the
|
|
72
|
+
* root namespace, the authoritative signal). `ribbon` is the parsed `<ribbon>` subtree, or `undefined`
|
|
73
|
+
* when the document customises only backstage/QAT/commands (which v1 does not parse). Future work can
|
|
74
|
+
* extend this with `backstage`/`qat` without changing the shape callers already depend on.
|
|
75
|
+
*/
|
|
76
|
+
export interface CustomUiDocument {
|
|
77
|
+
readonly dialect: RibbonDialect;
|
|
78
|
+
readonly ribbon?: Ribbon;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Parse a `customUI` part (raw UTF-8 bytes or its decoded text) into a {@link CustomUiDocument}.
|
|
82
|
+
*
|
|
83
|
+
* @throws {@link CustomUiParseError} if the XML is malformed, the root is not a `<customUI>` element in
|
|
84
|
+
* a recognised namespace, or the tree nests beyond {@link MAX_DEPTH}.
|
|
85
|
+
*/
|
|
86
|
+
export declare function parseCustomUi(input: string | Uint8Array): CustomUiDocument;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// Turn a `customUI` ribbon-customisation part into a typed, read-only view of the ribbon tree.
|
|
2
|
+
//
|
|
3
|
+
// A `.xlsm` can carry two ribbon parts hung off the *package root* rels: `customUI/customUI.xml`
|
|
4
|
+
// (Office 2007 RibbonX, namespace `.../2006/01/customui`) and `customUI/customUI14.xml` (Office 2010+,
|
|
5
|
+
// namespace `.../2009/07/customui`, which adds backstage/QAT/commands). Both are preserved verbatim on
|
|
6
|
+
// round-trip (see `src/core/preserved.ts`); this module adds a reader on top, mirroring how
|
|
7
|
+
// `parseVbaProject` projects a read view over bytes the writer already round-trips opaquely.
|
|
8
|
+
//
|
|
9
|
+
// Scope (v1): the `<ribbon>` subtree only — tabs → groups → controls, plus each control's callback
|
|
10
|
+
// names (the `onAction` a click invokes is the whole reason a macro workbook ships a ribbon). A
|
|
11
|
+
// customUI document's `<commands>`, `<backstage>`, `<contextMenus>`, and the ribbon's `qat`/
|
|
12
|
+
// `contextualTabs` are NOT parsed; they still round-trip byte-for-byte, they are just not surfaced here.
|
|
13
|
+
//
|
|
14
|
+
// Security posture: the part is untrusted input. The underlying `xmlEvents` scanner never expands
|
|
15
|
+
// entities or DTDs (see `xml-read.ts`); on top of that this parser caps nesting depth and fails closed
|
|
16
|
+
// with {@link CustomUiParseError} on any malformed or unrecognised structure rather than returning a
|
|
17
|
+
// half-built tree.
|
|
18
|
+
import { strFromU8 } from 'fflate';
|
|
19
|
+
import { boolStrict, localName, xmlEvents } from "../xml/xml-read.js";
|
|
20
|
+
import { CustomUiParseError } from "./errors.js";
|
|
21
|
+
/** The `customUI` root namespaces, one per {@link RibbonDialect}. */
|
|
22
|
+
export const CUSTOMUI_2006_NAMESPACE = 'http://schemas.microsoft.com/office/2006/01/customui';
|
|
23
|
+
export const CUSTOMUI_2009_NAMESPACE = 'http://schemas.microsoft.com/office/2009/07/customui';
|
|
24
|
+
// The OPC relationship Type URIs Office wires the two ribbon parts under, from the package-root rels.
|
|
25
|
+
// Both end `/ui/extensibility` (the 2010 one confusingly carries `2007` in its path); the reader
|
|
26
|
+
// matches on that suffix — {@link isCustomUiRelType} — exactly as the rest of the reader matches
|
|
27
|
+
// preserved relationship types by local suffix.
|
|
28
|
+
export const CUSTOMUI_2007_REL_TYPE = 'http://schemas.microsoft.com/office/2006/relationships/ui/extensibility';
|
|
29
|
+
export const CUSTOMUI_2010_REL_TYPE = 'http://schemas.microsoft.com/office/2007/relationships/ui/extensibility';
|
|
30
|
+
/** Whether a package-root relationship Type URI points at a `customUI` ribbon part. */
|
|
31
|
+
export function isCustomUiRelType(type) {
|
|
32
|
+
return type.endsWith('/ui/extensibility');
|
|
33
|
+
}
|
|
34
|
+
// A hostile part could nest elements thousands deep; the recursive control mapper would then overflow
|
|
35
|
+
// the call stack with an uncatchable RangeError. Capping tree depth keeps every failure a
|
|
36
|
+
// CustomUiParseError. Real ribbons nest only a handful deep (tab > group > menu > submenu), so this
|
|
37
|
+
// bound is unreachable by any legitimate document.
|
|
38
|
+
const MAX_DEPTH = 256;
|
|
39
|
+
const KNOWN_KINDS = new Set([
|
|
40
|
+
'button',
|
|
41
|
+
'toggleButton',
|
|
42
|
+
'checkBox',
|
|
43
|
+
'editBox',
|
|
44
|
+
'dropDown',
|
|
45
|
+
'comboBox',
|
|
46
|
+
'gallery',
|
|
47
|
+
'menu',
|
|
48
|
+
'dynamicMenu',
|
|
49
|
+
'splitButton',
|
|
50
|
+
'buttonGroup',
|
|
51
|
+
'box',
|
|
52
|
+
'labelControl',
|
|
53
|
+
'separator',
|
|
54
|
+
'menuSeparator',
|
|
55
|
+
'dialogBoxLauncher',
|
|
56
|
+
'control',
|
|
57
|
+
'item',
|
|
58
|
+
]);
|
|
59
|
+
/**
|
|
60
|
+
* Parse a `customUI` part (raw UTF-8 bytes or its decoded text) into a {@link CustomUiDocument}.
|
|
61
|
+
*
|
|
62
|
+
* @throws {@link CustomUiParseError} if the XML is malformed, the root is not a `<customUI>` element in
|
|
63
|
+
* a recognised namespace, or the tree nests beyond {@link MAX_DEPTH}.
|
|
64
|
+
*/
|
|
65
|
+
export function parseCustomUi(input) {
|
|
66
|
+
const xml = typeof input === 'string' ? input : strFromU8(input);
|
|
67
|
+
let root;
|
|
68
|
+
try {
|
|
69
|
+
root = buildTree(xml);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof CustomUiParseError)
|
|
73
|
+
throw error;
|
|
74
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
75
|
+
throw new CustomUiParseError(`malformed customUI XML: ${detail}`, { cause: error });
|
|
76
|
+
}
|
|
77
|
+
const customUi = findChild(root, 'customUI');
|
|
78
|
+
if (customUi === undefined) {
|
|
79
|
+
throw new CustomUiParseError('part has no <customUI> root element');
|
|
80
|
+
}
|
|
81
|
+
const dialect = dialectOf(customUi);
|
|
82
|
+
const ribbonEl = findChild(customUi, 'ribbon');
|
|
83
|
+
return ribbonEl === undefined ? { dialect } : { dialect, ribbon: toRibbon(ribbonEl) };
|
|
84
|
+
}
|
|
85
|
+
// Resolve the namespace bound to the customUI element's own name and map it to a dialect. A default
|
|
86
|
+
// namespace (`xmlns=`) governs an unprefixed `<customUI>`; a prefixed `<p:customUI>` is governed by
|
|
87
|
+
// that prefix's `xmlns:p`. Neither of the two known namespaces present means this is not a customUI part
|
|
88
|
+
// this reader understands — fail closed rather than guess.
|
|
89
|
+
function dialectOf(customUi) {
|
|
90
|
+
const colon = customUi.name.indexOf(':');
|
|
91
|
+
const nsAttr = colon === -1 ? 'xmlns' : `xmlns:${customUi.name.slice(0, colon)}`;
|
|
92
|
+
const ns = customUi.attrs[nsAttr];
|
|
93
|
+
if (ns === CUSTOMUI_2006_NAMESPACE)
|
|
94
|
+
return '2007';
|
|
95
|
+
if (ns === CUSTOMUI_2009_NAMESPACE)
|
|
96
|
+
return '2010';
|
|
97
|
+
throw new CustomUiParseError(`unrecognised customUI namespace: ${ns ?? '(none)'}`);
|
|
98
|
+
}
|
|
99
|
+
function toRibbon(ribbonEl) {
|
|
100
|
+
const tabsEl = findChild(ribbonEl, 'tabs');
|
|
101
|
+
const tabs = tabsEl === undefined ? [] : childrenNamed(tabsEl, 'tab').map(toTab);
|
|
102
|
+
return { startFromScratch: boolStrict(ribbonEl.attrs.startFromScratch), tabs };
|
|
103
|
+
}
|
|
104
|
+
function toTab(tabEl) {
|
|
105
|
+
return {
|
|
106
|
+
...identity(tabEl.attrs),
|
|
107
|
+
attributes: tabEl.attrs,
|
|
108
|
+
groups: childrenNamed(tabEl, 'group').map(toGroup),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function toGroup(groupEl) {
|
|
112
|
+
return {
|
|
113
|
+
...identity(groupEl.attrs),
|
|
114
|
+
attributes: groupEl.attrs,
|
|
115
|
+
controls: groupEl.children.map((child) => toControl(child)),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function toControl(el) {
|
|
119
|
+
const kind = KNOWN_KINDS.has(el.local)
|
|
120
|
+
? el.local
|
|
121
|
+
: 'unknown';
|
|
122
|
+
return {
|
|
123
|
+
kind,
|
|
124
|
+
...identity(el.attrs),
|
|
125
|
+
...(el.attrs.onAction !== undefined ? { onAction: el.attrs.onAction } : {}),
|
|
126
|
+
attributes: el.attrs,
|
|
127
|
+
...(el.children.length > 0 ? { children: el.children.map((child) => toControl(child)) } : {}),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
// The id/label attributes lifted onto every tab, group, and control. Emitted only when present, so an
|
|
131
|
+
// absent attribute stays absent (exactOptionalPropertyTypes) rather than becoming an explicit undefined.
|
|
132
|
+
function identity(attrs) {
|
|
133
|
+
return {
|
|
134
|
+
...(attrs.id !== undefined ? { id: attrs.id } : {}),
|
|
135
|
+
...(attrs.idQ !== undefined ? { idQ: attrs.idQ } : {}),
|
|
136
|
+
...(attrs.idMso !== undefined ? { idMso: attrs.idMso } : {}),
|
|
137
|
+
...(attrs.label !== undefined ? { label: attrs.label } : {}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// Build the element tree from the SAX event stream with an explicit stack (no recursion), capping depth
|
|
141
|
+
// so a hostile part cannot force the later recursive walk to overflow. Text and comments carry no ribbon
|
|
142
|
+
// meaning and are ignored. Throws CustomUiParseError on unbalanced markup; a lower-level SyntaxError from
|
|
143
|
+
// the scanner is caught and re-wrapped by the caller.
|
|
144
|
+
function buildTree(xml) {
|
|
145
|
+
const root = { name: '#root', local: '#root', attrs: {}, children: [] };
|
|
146
|
+
const stack = [root];
|
|
147
|
+
for (const event of xmlEvents(xml)) {
|
|
148
|
+
if (event.kind === 'open') {
|
|
149
|
+
const parent = stack[stack.length - 1];
|
|
150
|
+
if (parent === undefined)
|
|
151
|
+
throw new CustomUiParseError('unbalanced customUI markup');
|
|
152
|
+
const el = {
|
|
153
|
+
name: event.name,
|
|
154
|
+
local: localName(event.name),
|
|
155
|
+
attrs: event.attrs,
|
|
156
|
+
children: [],
|
|
157
|
+
};
|
|
158
|
+
parent.children.push(el);
|
|
159
|
+
if (!event.selfClosing) {
|
|
160
|
+
if (stack.length > MAX_DEPTH)
|
|
161
|
+
throw new CustomUiParseError('customUI nesting too deep');
|
|
162
|
+
stack.push(el);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
else if (event.kind === 'close') {
|
|
166
|
+
if (stack.length <= 1)
|
|
167
|
+
throw new CustomUiParseError('unbalanced customUI markup');
|
|
168
|
+
stack.pop();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
if (stack.length !== 1)
|
|
172
|
+
throw new CustomUiParseError('unbalanced customUI markup');
|
|
173
|
+
return root;
|
|
174
|
+
}
|
|
175
|
+
function findChild(el, local) {
|
|
176
|
+
return el.children.find((child) => child.local === local);
|
|
177
|
+
}
|
|
178
|
+
function childrenNamed(el, local) {
|
|
179
|
+
return el.children.filter((child) => child.local === local);
|
|
180
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { type CellAddress, columnToNumber, decodeAddress, decodeRange, encodeAddress, MAX_COLUMN, MAX_ROW, numberToColumn, type RangeAddress, } from '../core/address.ts';
|
|
2
|
+
export type { AutoFilter, CustomFilter, CustomFilterOperator, CustomFilterPredicate, FilterColumn, FilterCriteria, ValuesFilter, } from '../core/autofilter.ts';
|
|
3
|
+
export { Cell } from '../core/cell.ts';
|
|
4
|
+
export { applyTint, type ColorResolutionContext, DEFAULT_INDEXED_COLORS, resolveColor, SYSTEM_INDEXED_COLORS, } from '../core/color-resolution.ts';
|
|
5
|
+
export { Column } from '../core/column.ts';
|
|
6
|
+
export type { Comment, CommentThread, Mention, Person } from '../core/comment-thread.ts';
|
|
7
|
+
export type { CfValueObject, ConditionalFormatting, ConditionalFormattingRule, } from '../core/conditional-formatting.ts';
|
|
8
|
+
export type { DataValidation, DataValidationEntry, DataValidationErrorStyle, DataValidationOperator, DataValidationType, } from '../core/data-validation.ts';
|
|
9
|
+
export { type AnchoredImage, type AnchorPoint, type Extent, type ImageAnchor, type ImageEditAs, isOneCellAnchor, type OneCellAnchor, PX_TO_EMU, type TwoCellAnchor, type WorkbookImage, } from '../core/image.ts';
|
|
10
|
+
export type { HeaderFooter, PageBreak, PageMargins, PageSetup, PrintOptions, } from '../core/page-setup.ts';
|
|
11
|
+
export { type ParsedPivotField, type ParsedPivotSource, type ParsedPivotTable, type PivotCacheField, type PivotItem, type PivotMetric, type PivotNumericSummary, type PivotRecordCell, type PivotSourceKind, PivotTable, type PivotTableOptions, } from '../core/pivot-table.ts';
|
|
12
|
+
export type { PreservedPart, PreservedRelationship, PreservedRootReference, PreservedWorksheetReference, } from '../core/preserved.ts';
|
|
13
|
+
export type { SheetProtection, SheetProtectionCredential, SheetProtectionFlags, SheetProtectionOptions, } from '../core/protection.ts';
|
|
14
|
+
export { Range } from '../core/range.ts';
|
|
15
|
+
export { Row } from '../core/row.ts';
|
|
16
|
+
export type { Alignment, Border, BorderEdge, BorderStyle, CellStyle, Color, DifferentialStyle, Fill, FillPatternType, Font, FontScheme, FontVerticalAlignment, GradientFill, GradientStop, HorizontalAlignment, NamedCellStyle, PatternFill, Protection, TableStyleNamespace, TableStyleTable, UnderlineStyle, VerticalAlignment, } from '../core/style.ts';
|
|
17
|
+
export { Table, type TableColumn, type TableColumnStyle, type TableOptions, type TableRegion, type TableStyleInfo, } from '../core/table.ts';
|
|
18
|
+
export { isTableStyleElementType, STRIPE_ELEMENT_TYPES, TABLE_STYLE_ELEMENT_TYPES, type TableStyle, type TableStyleElement, type TableStyleElementType, } from '../core/table-style.ts';
|
|
19
|
+
export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, parseThemeColorScheme, THEME_COLOR_SLOTS, type ThemeColorScheme, type ThemeColorSlot, type ThemeFontScheme, type ThemeOverrides, } from '../core/theme.ts';
|
|
20
|
+
export { type CellValue, coerceCellValue, type DataTableFormulaValue, detectValueType, ERROR_CODES, type ErrorCode, type ErrorValue, type FormulaResult, type FormulaValue, type HyperlinkValue, isErrorCode, type RichTextRun, type RichTextValue, richTextToPlain, type SharedFormulaValue, ValueType, } from '../core/value.ts';
|
|
21
|
+
export { type AddImageOptions, type AddWorksheetOptions, DEFAULT_WORKBOOK_VIEW, type DefinedName, type PreservedWorkbookReference, Workbook, type WorkbookProperties, type WorkbookView, } from '../core/workbook.ts';
|
|
22
|
+
export type { WorkbookProtection, WorkbookProtectionCredentialAttr, } from '../core/workbook-protection.ts';
|
|
23
|
+
export { type CellModel, type ColumnProperties, type OutlineProperties, type RowInput, type RowProperties, type SheetView, Worksheet, type WorksheetModel, type WorksheetProperties, type WorksheetState, } from '../core/worksheet.ts';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Entry barrel for `@shbernal/ts-xlsx/core` — the document model, with no serialisation attached.
|
|
2
|
+
//
|
|
3
|
+
// This is what a consumer imports to build or inspect a workbook in memory: the classes, the
|
|
4
|
+
// property shapes, and the value vocabulary. It pulls in no ZIP, no XML writer and no BIFF12
|
|
5
|
+
// decoder. Errors are not here — the whole failure taxonomy lives behind `/errors`, so that a
|
|
6
|
+
// caller who only needs to branch on a failure never loads a codec to get the class.
|
|
7
|
+
export { columnToNumber, decodeAddress, decodeRange, encodeAddress, MAX_COLUMN, MAX_ROW, numberToColumn, } from "../core/address.js";
|
|
8
|
+
export { Cell } from "../core/cell.js";
|
|
9
|
+
export { applyTint, DEFAULT_INDEXED_COLORS, resolveColor, SYSTEM_INDEXED_COLORS, } from "../core/color-resolution.js";
|
|
10
|
+
export { Column } from "../core/column.js";
|
|
11
|
+
export { isOneCellAnchor, PX_TO_EMU, } from "../core/image.js";
|
|
12
|
+
export { PivotTable, } from "../core/pivot-table.js";
|
|
13
|
+
export { Range } from "../core/range.js";
|
|
14
|
+
export { Row } from "../core/row.js";
|
|
15
|
+
export { Table, } from "../core/table.js";
|
|
16
|
+
export { isTableStyleElementType, STRIPE_ELEMENT_TYPES, TABLE_STYLE_ELEMENT_TYPES, } from "../core/table-style.js";
|
|
17
|
+
export { DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, parseThemeColorScheme, THEME_COLOR_SLOTS, } from "../core/theme.js";
|
|
18
|
+
export { coerceCellValue, detectValueType, ERROR_CODES, isErrorCode, richTextToPlain, ValueType, } from "../core/value.js";
|
|
19
|
+
export { DEFAULT_WORKBOOK_VIEW, Workbook, } from "../core/workbook.js";
|
|
20
|
+
export { Worksheet, } from "../core/worksheet.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Entry barrel for `@shbernal/ts-xlsx/csv` — delimited text in and out.
|
|
2
|
+
//
|
|
3
|
+
// The two directions are asymmetric in what they cost: writing needs only a worksheet and the
|
|
4
|
+
// value vocabulary, while reading builds a `Workbook` and therefore pulls the model in whole.
|
|
5
|
+
export { readCsv } from "../io/csv/read.js";
|
|
6
|
+
export { writeCsv, writeCsvText } from "../io/csv/write.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { CUSTOMUI_2006_NAMESPACE, CUSTOMUI_2007_REL_TYPE, CUSTOMUI_2009_NAMESPACE, CUSTOMUI_2010_REL_TYPE, type CustomUiDocument, isCustomUiRelType, parseCustomUi, type Ribbon, type RibbonControl, type RibbonControlKind, type RibbonDialect, type RibbonGroup, type RibbonTab, } from '../customui/ribbon.ts';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// Entry barrel for `@shbernal/ts-xlsx/customui` — the read-only ribbon view over a macro
|
|
2
|
+
// workbook's preserved `customUI` parts.
|
|
3
|
+
export { CUSTOMUI_2006_NAMESPACE, CUSTOMUI_2007_REL_TYPE, CUSTOMUI_2009_NAMESPACE, CUSTOMUI_2010_REL_TYPE, isCustomUiRelType, parseCustomUi, } from "../customui/ribbon.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { CustomUiParseError } from '../customui/errors.ts';
|
|
2
|
+
export { AuthoringError, InternalError, XlsxError, type XlsxErrorCode } from '../errors.ts';
|
|
3
|
+
export { PackageReadError, type UnsupportedFormat, UnsupportedFormatError, } from '../io/opc/errors.ts';
|
|
4
|
+
export { XlsbParseError } from '../io/xlsb/errors.ts';
|
|
5
|
+
export { XlsxParseError } from '../io/xlsx/errors.ts';
|
|
6
|
+
export { VbaAuthorError, VbaParseError } from '../vba/errors.ts';
|
|
7
|
+
export { XmlParseError } from '../xml/errors.ts';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Entry barrel for `@shbernal/ts-xlsx/errors` — the whole failure taxonomy, and nothing else.
|
|
2
|
+
//
|
|
3
|
+
// Every error class the library throws is exported here and from no other entry. That rule is
|
|
4
|
+
// what keeps the entry barrels disjoint (a container-level failure belongs to no single codec:
|
|
5
|
+
// `readXlsx` and `readXlsb` both raise `UnsupportedFormatError`), and it makes the answer to
|
|
6
|
+
// "what can this throw at me?" one import rather than a hunt across codecs.
|
|
7
|
+
//
|
|
8
|
+
// It is also the cheapest thing in the package by an order of magnitude: the classes reach
|
|
9
|
+
// nothing but each other, so a service that only needs to classify a failure — log it, map it to
|
|
10
|
+
// an HTTP status, decide whether to retry — pays for the taxonomy and not for a parser.
|
|
11
|
+
//
|
|
12
|
+
// `XlsxError` is the one-line answer to "was that us?"; `XlsxErrorCode` is the kind of failure,
|
|
13
|
+
// shared across classes on purpose (see `src/errors.ts`).
|
|
14
|
+
export { CustomUiParseError } from "../customui/errors.js";
|
|
15
|
+
export { AuthoringError, InternalError, XlsxError } from "../errors.js";
|
|
16
|
+
export { PackageReadError, UnsupportedFormatError, } from "../io/opc/errors.js";
|
|
17
|
+
export { XlsbParseError } from "../io/xlsb/errors.js";
|
|
18
|
+
export { XlsxParseError } from "../io/xlsx/errors.js";
|
|
19
|
+
export { VbaAuthorError, VbaParseError } from "../vba/errors.js";
|
|
20
|
+
export { XmlParseError } from "../xml/errors.js";
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Entry barrel for `@shbernal/ts-xlsx/vba` — the native VBA read view and the structural edits.
|
|
2
|
+
//
|
|
3
|
+
// A deliberately narrower face than `src/vba/index.ts`, which is the *internal* barrel and also
|
|
4
|
+
// carries the CFB writer, the MS-OVBA container primitives and the part-path constants that
|
|
5
|
+
// `Workbook` and the codecs need. Those are implementation, not API.
|
|
6
|
+
export { parseVbaProject, } from "../vba/project.js";
|
|
7
|
+
export { addVbaReference, removeVbaModule, } from "../vba/project-editor.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { readXlsb } from '../io/xlsb/read.ts';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Entry barrel for `@shbernal/ts-xlsx/xlsb` — the BIFF12 reader.
|
|
2
|
+
//
|
|
3
|
+
// `readXlsx` auto-detects and reads a binary `.xlsb` too, so most callers never name this; it is
|
|
4
|
+
// public for the caller that holds bytes it already knows to be `.xlsb` and wants to say so. That
|
|
5
|
+
// dispatch also means this entry is not the way to *avoid* the BIFF12 codec — importing
|
|
6
|
+
// `/xlsx` loads it either way (see the per-entry budgets in `scripts/size-budget.ts`).
|
|
7
|
+
export { readXlsb } from "../io/xlsb/read.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { editXlsxVbaAddReference, editXlsxVbaRemoveModule } from '../io/xlsx/edit-vba.ts';
|
|
2
|
+
export { type ReadXlsxOptions, readXlsx } from '../io/xlsx/read.ts';
|
|
3
|
+
export { type ReadSheetRowsOptions, readSheetRows, readWorkbookStream, } from '../io/xlsx/read-rows.ts';
|
|
4
|
+
export { type WriteOptions, writeXlsx, writeXlsxAsync } from '../io/xlsx/write.ts';
|
|
5
|
+
export { type CalcProperties, StreamedRow, WorkbookStreamWriter, type WorkbookStreamWriterOptions, WorksheetStreamWriter, } from '../io/xlsx/write-stream.ts';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Entry barrel for `@shbernal/ts-xlsx/xlsx` — the XML codec: whole-workbook read and write, the
|
|
2
|
+
// streaming pair, and the in-place VBA part edits.
|
|
3
|
+
//
|
|
4
|
+
// The streaming reader's entry points are public; the granular per-row/cell/sheet output shapes
|
|
5
|
+
// (`StreamedRow`/`StreamedCell`/`StreamedSheet` as *read*) are intentionally left as inferred
|
|
6
|
+
// structural types rather than named commitments while that surface settles. The streaming
|
|
7
|
+
// *writer*'s whole surface is named: its incremental workbook/worksheet/row handles are classes
|
|
8
|
+
// and their options are interfaces, so there is nothing structural left un-named. A styled row
|
|
9
|
+
// hands back `Cell`, which belongs to `/core`.
|
|
10
|
+
//
|
|
11
|
+
// Streaming is not its own entry point. Measured, `read-rows` + `write-stream` reach every module
|
|
12
|
+
// `read` + `write` do plus three — an entry that costs what the codec costs is an alias, not a
|
|
13
|
+
// packaging boundary.
|
|
14
|
+
export { editXlsxVbaAddReference, editXlsxVbaRemoveModule } from "../io/xlsx/edit-vba.js";
|
|
15
|
+
export { readXlsx } from "../io/xlsx/read.js";
|
|
16
|
+
export { readSheetRows, readWorkbookStream, } from "../io/xlsx/read-rows.js";
|
|
17
|
+
export { writeXlsx, writeXlsxAsync } from "../io/xlsx/write.js";
|
|
18
|
+
export { StreamedRow, WorkbookStreamWriter, WorksheetStreamWriter, } from "../io/xlsx/write-stream.js";
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What kind of failure an {@link XlsxError} reports. This is the branch most callers want, and it is
|
|
3
|
+
* deliberately coarse: the four answers are the four different things a caller would *do* next.
|
|
4
|
+
*
|
|
5
|
+
* - `'unsupported-format'` — the input is not a container this library reads at all (a legacy `.xls`,
|
|
6
|
+
* a blob that is not a spreadsheet). Nothing is wrong with the file; it is the wrong file *for us*.
|
|
7
|
+
* - `'malformed-input'` — a part we do read is corrupt or does not conform to its specification. The
|
|
8
|
+
* file is broken, or hostile.
|
|
9
|
+
* - `'authoring'` — the caller described a document that cannot exist. The bug is in the calling code.
|
|
10
|
+
* - `'internal'` — an invariant this library maintains did not hold. It should be unreachable; if it
|
|
11
|
+
* fires, the bug is ours.
|
|
12
|
+
*
|
|
13
|
+
* There is deliberately no "not implemented yet" code. Every candidate for one turned out to be an
|
|
14
|
+
* unreachable exhaustiveness guard (so: `'internal'`), and the one genuine feature gap — a binary
|
|
15
|
+
* `.xlsb` cannot be row-streamed — is already reported by {@link UnsupportedFormatError}'s `format`
|
|
16
|
+
* branch. A code with no throw site would be a promise the library does not keep.
|
|
17
|
+
*/
|
|
18
|
+
export type XlsxErrorCode = 'unsupported-format' | 'malformed-input' | 'authoring' | 'internal';
|
|
19
|
+
/**
|
|
20
|
+
* The common ancestor of every error this library raises deliberately. `catch (e) { if (e instanceof
|
|
21
|
+
* XlsxError) … }` is the one-line answer to "was that us?", and {@link code} then says which kind of
|
|
22
|
+
* failure it was without naming a single subclass.
|
|
23
|
+
*
|
|
24
|
+
* Abstract on purpose: a failure always has a kind, so there is never a reason to throw the base.
|
|
25
|
+
* Every subclass fixes {@link code} to a literal, which makes the class hierarchy a discriminated
|
|
26
|
+
* union — narrowing on `error.code` narrows the type.
|
|
27
|
+
*
|
|
28
|
+
* The constructor is inherited from `Error`, so every subclass accepts `{cause}`; layers that wrap a
|
|
29
|
+
* lower-level failure are expected to pass it rather than flatten it into the message.
|
|
30
|
+
*/
|
|
31
|
+
export declare abstract class XlsxError extends Error {
|
|
32
|
+
abstract readonly code: XlsxErrorCode;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Thrown when the caller describes a document that cannot exist: a pivot table with no row field, a
|
|
36
|
+
* table whose columns do not span its range, a merge that overlaps another, a workbook with no
|
|
37
|
+
* worksheets. The document model, not a single argument, is what is wrong.
|
|
38
|
+
*
|
|
39
|
+
* **Where the line falls against native errors.** A single scalar that is out of range, unparseable,
|
|
40
|
+
* or the wrong type stays a native `RangeError` / `SyntaxError` / `TypeError` — those types exist for
|
|
41
|
+
* exactly that, every caller already reads them, and wrapping them would make this taxonomy a
|
|
42
|
+
* re-implementation of the language's. `AuthoringError` starts where a *composite* is internally
|
|
43
|
+
* inconsistent, or contradicts something a workbook can express. `getColumn(0)` is a `RangeError`;
|
|
44
|
+
* a table that names a column twice is an `AuthoringError`.
|
|
45
|
+
*
|
|
46
|
+
* It is always the calling code that is wrong, never the input file — a malformed file raises a
|
|
47
|
+
* `'malformed-input'` error instead.
|
|
48
|
+
*/
|
|
49
|
+
export declare class AuthoringError extends XlsxError {
|
|
50
|
+
readonly name = "AuthoringError";
|
|
51
|
+
readonly code = "authoring";
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Thrown where an invariant the library itself maintains turns out not to hold — typically an index
|
|
55
|
+
* that a preceding pass proved to be in range, re-checked because `noUncheckedIndexedAccess` makes
|
|
56
|
+
* the possibility of `undefined` explicit and casting it away would be worse.
|
|
57
|
+
*
|
|
58
|
+
* No caller can provoke one, so it is not a failure mode to handle: seeing it means the bug is ours.
|
|
59
|
+
* It exists as a distinct type so that "unreachable" is *stated* rather than implied by a bare
|
|
60
|
+
* `Error`, which reads identically to a throw nobody has classified yet.
|
|
61
|
+
*/
|
|
62
|
+
export declare class InternalError extends XlsxError {
|
|
63
|
+
readonly name = "InternalError";
|
|
64
|
+
readonly code = "internal";
|
|
65
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// The failure taxonomy every layer throws through.
|
|
2
|
+
//
|
|
3
|
+
// Typed error classes grew up one per subsystem that happened to want one, sharing no ancestor, so a
|
|
4
|
+
// caller could not ask "did *this library* fail?" without naming all of them — while the model's own
|
|
5
|
+
// validation threw bare `Error`, distinguishable only by string-matching the message. For a library
|
|
6
|
+
// whose input is untrusted files, "was it my file or my call?" is a first-class question, and it had
|
|
7
|
+
// no answer.
|
|
8
|
+
//
|
|
9
|
+
// Two levels of branch, deliberately non-redundant:
|
|
10
|
+
// - `code` says what *kind* of failure this is — the question a caller usually has.
|
|
11
|
+
// - `name` (and `instanceof`) says exactly which one — the question a caller occasionally has.
|
|
12
|
+
// A code shared by several classes is the design, not an oversight: a `code` in 1:1 correspondence
|
|
13
|
+
// with the classes would carry nothing the class did not already carry.
|
|
14
|
+
//
|
|
15
|
+
// Scalar argument validation stays *outside* this taxonomy — see {@link AuthoringError} for where the
|
|
16
|
+
// line falls and why.
|
|
17
|
+
//
|
|
18
|
+
// This module sits below every layer that throws (`src/xml`, `src/core`, `src/io`, `src/vba`,
|
|
19
|
+
// `src/customui` all import it), so it imports nothing itself; `scripts/check-layering.ts` enforces
|
|
20
|
+
// that.
|
|
21
|
+
/**
|
|
22
|
+
* The common ancestor of every error this library raises deliberately. `catch (e) { if (e instanceof
|
|
23
|
+
* XlsxError) … }` is the one-line answer to "was that us?", and {@link code} then says which kind of
|
|
24
|
+
* failure it was without naming a single subclass.
|
|
25
|
+
*
|
|
26
|
+
* Abstract on purpose: a failure always has a kind, so there is never a reason to throw the base.
|
|
27
|
+
* Every subclass fixes {@link code} to a literal, which makes the class hierarchy a discriminated
|
|
28
|
+
* union — narrowing on `error.code` narrows the type.
|
|
29
|
+
*
|
|
30
|
+
* The constructor is inherited from `Error`, so every subclass accepts `{cause}`; layers that wrap a
|
|
31
|
+
* lower-level failure are expected to pass it rather than flatten it into the message.
|
|
32
|
+
*/
|
|
33
|
+
export class XlsxError extends Error {
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Thrown when the caller describes a document that cannot exist: a pivot table with no row field, a
|
|
37
|
+
* table whose columns do not span its range, a merge that overlaps another, a workbook with no
|
|
38
|
+
* worksheets. The document model, not a single argument, is what is wrong.
|
|
39
|
+
*
|
|
40
|
+
* **Where the line falls against native errors.** A single scalar that is out of range, unparseable,
|
|
41
|
+
* or the wrong type stays a native `RangeError` / `SyntaxError` / `TypeError` — those types exist for
|
|
42
|
+
* exactly that, every caller already reads them, and wrapping them would make this taxonomy a
|
|
43
|
+
* re-implementation of the language's. `AuthoringError` starts where a *composite* is internally
|
|
44
|
+
* inconsistent, or contradicts something a workbook can express. `getColumn(0)` is a `RangeError`;
|
|
45
|
+
* a table that names a column twice is an `AuthoringError`.
|
|
46
|
+
*
|
|
47
|
+
* It is always the calling code that is wrong, never the input file — a malformed file raises a
|
|
48
|
+
* `'malformed-input'` error instead.
|
|
49
|
+
*/
|
|
50
|
+
export class AuthoringError extends XlsxError {
|
|
51
|
+
name = 'AuthoringError';
|
|
52
|
+
code = 'authoring';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Thrown where an invariant the library itself maintains turns out not to hold — typically an index
|
|
56
|
+
* that a preceding pass proved to be in range, re-checked because `noUncheckedIndexedAccess` makes
|
|
57
|
+
* the possibility of `undefined` explicit and casting it away would be worse.
|
|
58
|
+
*
|
|
59
|
+
* No caller can provoke one, so it is not a failure mode to handle: seeing it means the bug is ours.
|
|
60
|
+
* It exists as a distinct type so that "unreachable" is *stated* rather than implied by a bare
|
|
61
|
+
* `Error`, which reads identically to a throw nobody has classified yet.
|
|
62
|
+
*/
|
|
63
|
+
export class InternalError extends XlsxError {
|
|
64
|
+
name = 'InternalError';
|
|
65
|
+
code = 'internal';
|
|
66
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// The convenience entry point: everything the package exports, under one specifier.
|
|
2
|
+
//
|
|
3
|
+
// It is a union of the subpath entry barrels in `src/entries/`, which are the real public faces —
|
|
4
|
+
// `@shbernal/ts-xlsx/core`, `/xlsx`, `/xlsb`, `/csv`, `/vba`, `/customui`, `/errors`. Each symbol
|
|
5
|
+
// is listed in exactly one of them, so there is no second list to keep in step here and a star
|
|
6
|
+
// re-export cannot silently drop a name to an ambiguity. `scripts/check-layering.ts` holds the
|
|
7
|
+
// entries disjoint and keeps this file's composition honest.
|
|
8
|
+
//
|
|
9
|
+
// Importing from here is the right default; it costs nothing extra to a bundler, because
|
|
10
|
+
// `sideEffects: false` lets an unused module be dropped whole. Reach for a subpath when the
|
|
11
|
+
// consumer has no bundler to do that for it, or when you want the module graph itself to say
|
|
12
|
+
// which half of the library a service depends on.
|
|
13
|
+
export * from "./entries/core.js";
|
|
14
|
+
export * from "./entries/csv.js";
|
|
15
|
+
export * from "./entries/customui.js";
|
|
16
|
+
export * from "./entries/errors.js";
|
|
17
|
+
export * from "./entries/vba.js";
|
|
18
|
+
export * from "./entries/xlsb.js";
|
|
19
|
+
export * from "./entries/xlsx.js";
|