@rdfx/xlsx 0.0.0-stage → 0.0.60
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/dist/AbstractParser.d.ts +20 -0
- package/dist/AbstractParser.js +15 -0
- package/dist/Cell.d.ts +64 -0
- package/dist/Cell.js +208 -0
- package/dist/CellAddress.d.ts +28 -0
- package/dist/CellAddress.js +77 -0
- package/dist/JsonResourceRecordParser.d.ts +32 -0
- package/dist/JsonResourceRecordParser.js +30 -0
- package/dist/LangString.d.ts +9 -0
- package/dist/LangString.js +9 -0
- package/dist/ResourceRecord.d.ts +6 -0
- package/dist/ResourceRecord.js +2 -0
- package/dist/ResourceRecordParser.d.ts +12 -0
- package/dist/ResourceRecordParser.js +2 -0
- package/dist/ResourceRecordParserFactory.d.ts +4 -0
- package/dist/ResourceRecordParserFactory.js +2 -0
- package/dist/ResourceWorkbookParser.d.ts +14 -0
- package/dist/ResourceWorkbookParser.js +37 -0
- package/dist/ResourceWorksheetParser.d.ts +15 -0
- package/dist/ResourceWorksheetParser.js +136 -0
- package/dist/Workbook.d.ts +23 -0
- package/dist/Workbook.js +88 -0
- package/dist/Worksheet.d.ts +60 -0
- package/dist/Worksheet.js +196 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/package.json +47 -4
- package/README.md +0 -3
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { NamespaceBuilder } from "@rdfjs/namespace";
|
|
2
|
+
import type PrefixMap from "@rdfjs/prefix-map/PrefixMap.js";
|
|
3
|
+
import type { DataFactory, DatasetCoreFactory } from "@rdfjs/types";
|
|
4
|
+
import type { Logger } from "@rdfx/logger";
|
|
5
|
+
import type { Maybe } from "purify-ts";
|
|
6
|
+
export declare abstract class AbstractParser {
|
|
7
|
+
protected readonly dataFactory: DataFactory;
|
|
8
|
+
protected readonly datasetFactory: DatasetCoreFactory;
|
|
9
|
+
protected readonly logger: Logger;
|
|
10
|
+
protected readonly prefixMap: Maybe<PrefixMap>;
|
|
11
|
+
protected readonly defaultNamespace: Maybe<NamespaceBuilder>;
|
|
12
|
+
constructor({ dataFactory, datasetFactory, defaultNamespace, logger, prefixMap, }: {
|
|
13
|
+
dataFactory: DataFactory;
|
|
14
|
+
datasetFactory: DatasetCoreFactory;
|
|
15
|
+
defaultNamespace: Maybe<NamespaceBuilder>;
|
|
16
|
+
logger: Logger;
|
|
17
|
+
prefixMap: Maybe<PrefixMap>;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=AbstractParser.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export class AbstractParser {
|
|
2
|
+
dataFactory;
|
|
3
|
+
datasetFactory;
|
|
4
|
+
logger;
|
|
5
|
+
prefixMap;
|
|
6
|
+
defaultNamespace;
|
|
7
|
+
constructor({ dataFactory, datasetFactory, defaultNamespace, logger, prefixMap, }) {
|
|
8
|
+
this.dataFactory = dataFactory;
|
|
9
|
+
this.datasetFactory = datasetFactory;
|
|
10
|
+
this.defaultNamespace = defaultNamespace;
|
|
11
|
+
this.logger = logger;
|
|
12
|
+
this.prefixMap = prefixMap;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=AbstractParser.js.map
|
package/dist/Cell.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type DepParser } from "fast-formula-parser";
|
|
2
|
+
import { Either, type Maybe } from "purify-ts";
|
|
3
|
+
import { CellAddress } from "./CellAddress.js";
|
|
4
|
+
import type { Worksheet } from "./Worksheet.js";
|
|
5
|
+
export declare class Cell {
|
|
6
|
+
#private;
|
|
7
|
+
private readonly depParser;
|
|
8
|
+
private readonly worksheet;
|
|
9
|
+
/**
|
|
10
|
+
* Address of this cell.
|
|
11
|
+
*/
|
|
12
|
+
readonly address: CellAddress;
|
|
13
|
+
/**
|
|
14
|
+
* Formula associated with this cell.
|
|
15
|
+
*/
|
|
16
|
+
readonly formula: Maybe<string>;
|
|
17
|
+
/**
|
|
18
|
+
* Hyperlink attached to this cell.
|
|
19
|
+
*/
|
|
20
|
+
readonly hyperlink: Maybe<Cell.Hyperlink>;
|
|
21
|
+
/**
|
|
22
|
+
* Value associated with this cell.
|
|
23
|
+
*/
|
|
24
|
+
readonly value: Maybe<Cell.Value>;
|
|
25
|
+
constructor({ address, depParser, formula, hyperlink, value, worksheet, }: {
|
|
26
|
+
address: CellAddress;
|
|
27
|
+
depParser: DepParser;
|
|
28
|
+
formula: Maybe<string>;
|
|
29
|
+
hyperlink: Maybe<Cell.Hyperlink>;
|
|
30
|
+
value: Maybe<Cell.Value>;
|
|
31
|
+
worksheet: Worksheet;
|
|
32
|
+
});
|
|
33
|
+
get dependencies(): Either<Error, (CellAddress | Cell)[]>;
|
|
34
|
+
get dependencyCellAddresses(): Either<Error, readonly CellAddress[]>;
|
|
35
|
+
/**
|
|
36
|
+
* SHA-256 sha256 hex digest of this cell.
|
|
37
|
+
*/
|
|
38
|
+
get sha256(): string;
|
|
39
|
+
toJSON(): Record<string, unknown>;
|
|
40
|
+
protected hashRecursive(visitedCellAddresses: Set<string>): string;
|
|
41
|
+
}
|
|
42
|
+
export declare namespace Cell {
|
|
43
|
+
class Error {
|
|
44
|
+
readonly value: string;
|
|
45
|
+
constructor(value: string);
|
|
46
|
+
}
|
|
47
|
+
interface Hyperlink {
|
|
48
|
+
readonly target: string;
|
|
49
|
+
readonly tooltip: Maybe<string>;
|
|
50
|
+
}
|
|
51
|
+
namespace Hyperlink {
|
|
52
|
+
function toJSON(hyperlink: Hyperlink): {
|
|
53
|
+
target: string;
|
|
54
|
+
tooltip?: string;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
type Value = boolean | Error | Date | number | string;
|
|
58
|
+
namespace Value {
|
|
59
|
+
function toJSON(value: Value): boolean | number | {
|
|
60
|
+
error: string;
|
|
61
|
+
} | string;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=Cell.d.ts.map
|
package/dist/Cell.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { MAX_COLUMN, MAX_ROW } from "fast-formula-parser";
|
|
8
|
+
import { sha256 } from "js-sha256";
|
|
9
|
+
import { Either } from "purify-ts";
|
|
10
|
+
import { Memoize } from "typescript-memoize";
|
|
11
|
+
import { CellAddress } from "./CellAddress.js";
|
|
12
|
+
export class Cell {
|
|
13
|
+
depParser;
|
|
14
|
+
worksheet;
|
|
15
|
+
#sha256 = null;
|
|
16
|
+
/**
|
|
17
|
+
* Address of this cell.
|
|
18
|
+
*/
|
|
19
|
+
address;
|
|
20
|
+
/**
|
|
21
|
+
* Formula associated with this cell.
|
|
22
|
+
*/
|
|
23
|
+
formula;
|
|
24
|
+
/**
|
|
25
|
+
* Hyperlink attached to this cell.
|
|
26
|
+
*/
|
|
27
|
+
hyperlink;
|
|
28
|
+
/**
|
|
29
|
+
* Value associated with this cell.
|
|
30
|
+
*/
|
|
31
|
+
value;
|
|
32
|
+
constructor({ address, depParser, formula, hyperlink, value, worksheet, }) {
|
|
33
|
+
this.address = address;
|
|
34
|
+
this.depParser = depParser;
|
|
35
|
+
this.formula = formula;
|
|
36
|
+
this.hyperlink = hyperlink;
|
|
37
|
+
this.value = value;
|
|
38
|
+
this.worksheet = worksheet;
|
|
39
|
+
}
|
|
40
|
+
get dependencies() {
|
|
41
|
+
return this.dependencyCellAddresses.map((dependencyCellAddresses) => dependencyCellAddresses.map((dependencyCellAddress) => this.worksheet.workbook.cell(dependencyCellAddress).orDefault(dependencyCellAddress)));
|
|
42
|
+
}
|
|
43
|
+
get dependencyCellAddresses() {
|
|
44
|
+
const formula = this.formula.extract();
|
|
45
|
+
if (!formula) {
|
|
46
|
+
return Either.of([]);
|
|
47
|
+
}
|
|
48
|
+
return Either.encase(() => {
|
|
49
|
+
const dependencyCellAddresses = [];
|
|
50
|
+
const deps = this.depParser.parse(formula, {
|
|
51
|
+
// DepParser uses 1-based row and column indices
|
|
52
|
+
col: this.address.columnIndex + 1,
|
|
53
|
+
row: this.address.rowIndex + 1,
|
|
54
|
+
sheet: this.worksheet.name,
|
|
55
|
+
});
|
|
56
|
+
for (const dep of deps) {
|
|
57
|
+
if ("from" in dep) {
|
|
58
|
+
for (let row = Math.min(dep.from.row, dep.to.row); row <= Math.max(dep.from.row, dep.to.row); row++) {
|
|
59
|
+
for (let col = Math.min(dep.from.col, dep.to.col); col <= Math.max(dep.from.col, dep.to.col); col++) {
|
|
60
|
+
dependencyCellAddresses.push(new CellAddress({
|
|
61
|
+
columnIndex: col - 1,
|
|
62
|
+
rowIndex: row - 1,
|
|
63
|
+
worksheetName: dep.sheet ?? this.worksheet.name,
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
if (dep.col === MAX_COLUMN || dep.row === MAX_ROW) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
dependencyCellAddresses.push(new CellAddress({
|
|
73
|
+
columnIndex: dep.col - 1,
|
|
74
|
+
rowIndex: dep.row - 1,
|
|
75
|
+
worksheetName: dep.sheet ?? this.worksheet.name,
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return dependencyCellAddresses;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* SHA-256 sha256 hex digest of this cell.
|
|
84
|
+
*/
|
|
85
|
+
get sha256() {
|
|
86
|
+
if (this.#sha256 !== null) {
|
|
87
|
+
return this.#sha256;
|
|
88
|
+
}
|
|
89
|
+
return this.hashRecursive(new Set());
|
|
90
|
+
}
|
|
91
|
+
toJSON() {
|
|
92
|
+
const json = {};
|
|
93
|
+
this.dependencyCellAddresses
|
|
94
|
+
.ifLeft((error) => {
|
|
95
|
+
json["dependencies"] = { error: error.message };
|
|
96
|
+
})
|
|
97
|
+
.ifRight((dependencies) => {
|
|
98
|
+
json["dependencies"] = dependencies;
|
|
99
|
+
});
|
|
100
|
+
this.formula.ifJust((formula) => {
|
|
101
|
+
json["formula"] = formula;
|
|
102
|
+
});
|
|
103
|
+
this.hyperlink.ifJust((hyperlink) => {
|
|
104
|
+
json["hyperlink"] = Cell.Hyperlink.toJSON(hyperlink);
|
|
105
|
+
});
|
|
106
|
+
this.value.ifJust((value) => {
|
|
107
|
+
json["value"] = Cell.Value.toJSON(value);
|
|
108
|
+
});
|
|
109
|
+
return json;
|
|
110
|
+
}
|
|
111
|
+
hashRecursive(visitedCellAddresses) {
|
|
112
|
+
if (this.#sha256 !== null) {
|
|
113
|
+
return this.#sha256;
|
|
114
|
+
}
|
|
115
|
+
visitedCellAddresses.add(this.address.string);
|
|
116
|
+
const circularDependencyCellAddresses = [];
|
|
117
|
+
const resolvedDependencyHashes = [];
|
|
118
|
+
const unresolvedDependencyCellAddresses = [];
|
|
119
|
+
for (const dependencyCellAddress of this.dependencyCellAddresses.orDefault([])) {
|
|
120
|
+
if (visitedCellAddresses.has(dependencyCellAddress.string)) {
|
|
121
|
+
circularDependencyCellAddresses.push(dependencyCellAddress.string);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
this.worksheet.workbook
|
|
125
|
+
.cell(dependencyCellAddress)
|
|
126
|
+
.ifJust((dependency) => {
|
|
127
|
+
if (dependency.formula.isJust()) {
|
|
128
|
+
resolvedDependencyHashes.push(dependency.hashRecursive(visitedCellAddresses));
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
resolvedDependencyHashes.push(dependency.sha256);
|
|
132
|
+
}
|
|
133
|
+
})
|
|
134
|
+
.ifNothing(() => {
|
|
135
|
+
unresolvedDependencyCellAddresses.push(dependencyCellAddress.string);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
visitedCellAddresses.delete(this.address.string);
|
|
139
|
+
this.#sha256 = sha256(JSON.stringify({
|
|
140
|
+
address: this.address.toJSON(),
|
|
141
|
+
circularDependencyCellAddresses: circularDependencyCellAddresses.length > 0
|
|
142
|
+
? circularDependencyCellAddresses
|
|
143
|
+
: undefined,
|
|
144
|
+
formula: this.formula.extract(),
|
|
145
|
+
hyperlink: this.hyperlink.map(Cell.Hyperlink.toJSON).extract(),
|
|
146
|
+
resolvedDependencyHashes: resolvedDependencyHashes.length > 0
|
|
147
|
+
? resolvedDependencyHashes
|
|
148
|
+
: undefined,
|
|
149
|
+
unresolvedDependencyCellAddresses: unresolvedDependencyCellAddresses.length > 0
|
|
150
|
+
? unresolvedDependencyCellAddresses
|
|
151
|
+
: undefined,
|
|
152
|
+
value: this.value.map(Cell.Value.toJSON).extract(),
|
|
153
|
+
}));
|
|
154
|
+
return this.#sha256;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
__decorate([
|
|
158
|
+
Memoize()
|
|
159
|
+
], Cell.prototype, "dependencies", null);
|
|
160
|
+
__decorate([
|
|
161
|
+
Memoize()
|
|
162
|
+
], Cell.prototype, "dependencyCellAddresses", null);
|
|
163
|
+
(function (Cell) {
|
|
164
|
+
// biome-ignore lint/suspicious/noShadowRestrictedNames: Only used here
|
|
165
|
+
class Error {
|
|
166
|
+
value;
|
|
167
|
+
constructor(value) {
|
|
168
|
+
this.value = value;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
Cell.Error = Error;
|
|
172
|
+
let Hyperlink;
|
|
173
|
+
(function (Hyperlink) {
|
|
174
|
+
function toJSON(hyperlink) {
|
|
175
|
+
return { target: hyperlink.target, tooltip: hyperlink.tooltip.extract() };
|
|
176
|
+
}
|
|
177
|
+
Hyperlink.toJSON = toJSON;
|
|
178
|
+
})(Hyperlink = Cell.Hyperlink || (Cell.Hyperlink = {}));
|
|
179
|
+
let Value;
|
|
180
|
+
(function (Value) {
|
|
181
|
+
function toJSON(value) {
|
|
182
|
+
switch (typeof value) {
|
|
183
|
+
case "boolean":
|
|
184
|
+
case "number":
|
|
185
|
+
case "string":
|
|
186
|
+
return value;
|
|
187
|
+
case "object":
|
|
188
|
+
if (value instanceof Date) {
|
|
189
|
+
return value.toISOString();
|
|
190
|
+
}
|
|
191
|
+
else if (value instanceof Cell.Error) {
|
|
192
|
+
return {
|
|
193
|
+
error: value.value,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
value;
|
|
198
|
+
throw new RangeError();
|
|
199
|
+
}
|
|
200
|
+
default:
|
|
201
|
+
value;
|
|
202
|
+
throw new RangeError(typeof value);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
Value.toJSON = toJSON;
|
|
206
|
+
})(Value = Cell.Value || (Cell.Value = {}));
|
|
207
|
+
})(Cell || (Cell = {}));
|
|
208
|
+
//# sourceMappingURL=Cell.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A cell address qualified with the worksheet name. Can be serialized with the .string method to "Sheet!A1".
|
|
3
|
+
*/
|
|
4
|
+
export declare class CellAddress {
|
|
5
|
+
/**
|
|
6
|
+
* Zero-based column index.
|
|
7
|
+
**/
|
|
8
|
+
readonly columnIndex: number;
|
|
9
|
+
/**
|
|
10
|
+
* Zero-based row index
|
|
11
|
+
*/
|
|
12
|
+
readonly rowIndex: number;
|
|
13
|
+
/**
|
|
14
|
+
* Worksheet name.
|
|
15
|
+
*/
|
|
16
|
+
readonly worksheetName: string;
|
|
17
|
+
constructor({ columnIndex, rowIndex, worksheetName, }: {
|
|
18
|
+
columnIndex: number;
|
|
19
|
+
rowIndex: number;
|
|
20
|
+
worksheetName: string;
|
|
21
|
+
});
|
|
22
|
+
get columnLetter(): string;
|
|
23
|
+
get string(): string;
|
|
24
|
+
static parse(cellAddress: string, worksheetName: string): CellAddress;
|
|
25
|
+
toJSON(): string;
|
|
26
|
+
toString(): string;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=CellAddress.d.ts.map
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { invariant } from "ts-invariant";
|
|
8
|
+
import { Memoize } from "typescript-memoize";
|
|
9
|
+
import * as xlsx from "xlsx";
|
|
10
|
+
/**
|
|
11
|
+
* A cell address qualified with the worksheet name. Can be serialized with the .string method to "Sheet!A1".
|
|
12
|
+
*/
|
|
13
|
+
export class CellAddress {
|
|
14
|
+
/**
|
|
15
|
+
* Zero-based column index.
|
|
16
|
+
**/
|
|
17
|
+
columnIndex;
|
|
18
|
+
/**
|
|
19
|
+
* Zero-based row index
|
|
20
|
+
*/
|
|
21
|
+
rowIndex;
|
|
22
|
+
/**
|
|
23
|
+
* Worksheet name.
|
|
24
|
+
*/
|
|
25
|
+
worksheetName;
|
|
26
|
+
constructor({ columnIndex, rowIndex, worksheetName, }) {
|
|
27
|
+
invariant(Number.isInteger(columnIndex) && columnIndex >= 0, columnIndex);
|
|
28
|
+
invariant(Number.isInteger(rowIndex) && rowIndex >= 0, rowIndex);
|
|
29
|
+
this.columnIndex = columnIndex;
|
|
30
|
+
this.rowIndex = rowIndex;
|
|
31
|
+
// Strip quotes off the worksheet name
|
|
32
|
+
if (worksheetName.length >= 2 &&
|
|
33
|
+
worksheetName.startsWith("'") &&
|
|
34
|
+
worksheetName.endsWith("'")) {
|
|
35
|
+
// unescape double single-quotes -> single quote
|
|
36
|
+
worksheetName = worksheetName.slice(1, -1).replace(/''/g, "'");
|
|
37
|
+
}
|
|
38
|
+
this.worksheetName = worksheetName;
|
|
39
|
+
}
|
|
40
|
+
get columnLetter() {
|
|
41
|
+
let letter = "";
|
|
42
|
+
let n = this.columnIndex + 1;
|
|
43
|
+
while (n > 0) {
|
|
44
|
+
letter = String.fromCharCode(((n - 1) % 26) + 65) + letter;
|
|
45
|
+
n = Math.floor((n - 1) / 26);
|
|
46
|
+
}
|
|
47
|
+
return letter;
|
|
48
|
+
}
|
|
49
|
+
get string() {
|
|
50
|
+
const quotedSheetName = /[^A-Za-z0-9_]/.test(this.worksheetName)
|
|
51
|
+
? // escape single quotes by doubling them
|
|
52
|
+
`'${this.worksheetName.replace(/'/g, "''")}'`
|
|
53
|
+
: this.worksheetName;
|
|
54
|
+
return `${quotedSheetName}!${this.columnLetter}${this.rowIndex + 1}`;
|
|
55
|
+
}
|
|
56
|
+
static parse(cellAddress, worksheetName) {
|
|
57
|
+
const decodedCellAddress = xlsx.utils.decode_cell(cellAddress); // { r: 0, c: 0 } (0-based)
|
|
58
|
+
return new CellAddress({
|
|
59
|
+
columnIndex: decodedCellAddress.c,
|
|
60
|
+
rowIndex: decodedCellAddress.r,
|
|
61
|
+
worksheetName: worksheetName,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
toJSON() {
|
|
65
|
+
return this.toString();
|
|
66
|
+
}
|
|
67
|
+
toString() {
|
|
68
|
+
return this.string;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
__decorate([
|
|
72
|
+
Memoize()
|
|
73
|
+
], CellAddress.prototype, "columnLetter", null);
|
|
74
|
+
__decorate([
|
|
75
|
+
Memoize()
|
|
76
|
+
], CellAddress.prototype, "string", null);
|
|
77
|
+
//# sourceMappingURL=CellAddress.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { NamespaceBuilder } from "@rdfjs/namespace";
|
|
2
|
+
import type PrefixMap from "@rdfjs/prefix-map/PrefixMap.js";
|
|
3
|
+
import type { Quad } from "@rdfjs/types";
|
|
4
|
+
import { type Resource, ResourceSet } from "@rdfx/resource";
|
|
5
|
+
import type { Either } from "purify-ts";
|
|
6
|
+
import { AbstractParser } from "./AbstractParser.js";
|
|
7
|
+
import type { ResourceRecord } from "./ResourceRecord.js";
|
|
8
|
+
import type { ResourceRecordParser } from "./ResourceRecordParser.js";
|
|
9
|
+
interface IntermediateObjectFunctions<IntermediateObjectT extends object, JsonObjectT extends object> {
|
|
10
|
+
Json: {
|
|
11
|
+
parse(json: unknown): Either<Error, JsonObjectT>;
|
|
12
|
+
};
|
|
13
|
+
fromJson: (jsonObject: JsonObjectT, options?: {
|
|
14
|
+
defaultNamespace?: NamespaceBuilder;
|
|
15
|
+
prefixMap?: PrefixMap;
|
|
16
|
+
}) => Either<Error, IntermediateObjectT>;
|
|
17
|
+
toRdfResource: (object: IntermediateObjectT, options?: {
|
|
18
|
+
resourceSet?: ResourceSet;
|
|
19
|
+
}) => Resource;
|
|
20
|
+
}
|
|
21
|
+
export declare class JsonResourceRecordParser<IntermediateObjectT extends object, JsonObjectT extends object> extends AbstractParser implements ResourceRecordParser {
|
|
22
|
+
private readonly intermediateObjectFunctions;
|
|
23
|
+
constructor(intermediateObjectFunctions: IntermediateObjectFunctions<IntermediateObjectT, JsonObjectT>, superParameters: ConstructorParameters<typeof AbstractParser>[0]);
|
|
24
|
+
/**
|
|
25
|
+
* Parse a ResourceRecord into an array of RDF Quads.
|
|
26
|
+
*
|
|
27
|
+
* Takes dataFactory, et al. parameters here rather than in the constructor in order to simplify construction.
|
|
28
|
+
*/
|
|
29
|
+
parse(resourceRecord: ResourceRecord): Either<Error, readonly Quad[]>;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=JsonResourceRecordParser.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { ResourceSet } from "@rdfx/resource";
|
|
2
|
+
import { AbstractParser } from "./AbstractParser.js";
|
|
3
|
+
export class JsonResourceRecordParser extends AbstractParser {
|
|
4
|
+
intermediateObjectFunctions;
|
|
5
|
+
constructor(intermediateObjectFunctions, superParameters) {
|
|
6
|
+
super(superParameters);
|
|
7
|
+
this.intermediateObjectFunctions = intermediateObjectFunctions;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Parse a ResourceRecord into an array of RDF Quads.
|
|
11
|
+
*
|
|
12
|
+
* Takes dataFactory, et al. parameters here rather than in the constructor in order to simplify construction.
|
|
13
|
+
*/
|
|
14
|
+
parse(resourceRecord) {
|
|
15
|
+
return this.intermediateObjectFunctions.Json.parse(resourceRecord).chain((jsonObject) => this.intermediateObjectFunctions
|
|
16
|
+
.fromJson(jsonObject, {
|
|
17
|
+
defaultNamespace: this.defaultNamespace.extract(),
|
|
18
|
+
prefixMap: this.prefixMap.extract(),
|
|
19
|
+
})
|
|
20
|
+
.map((object) => [
|
|
21
|
+
...this.intermediateObjectFunctions.toRdfResource(object, {
|
|
22
|
+
resourceSet: new ResourceSet({
|
|
23
|
+
dataFactory: this.dataFactory,
|
|
24
|
+
dataset: this.datasetFactory.dataset(),
|
|
25
|
+
}),
|
|
26
|
+
}).dataset,
|
|
27
|
+
]));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=JsonResourceRecordParser.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { LangString } from "./LangString.js";
|
|
2
|
+
export type ResourceRecord = Record<string, ResourceRecord.Value | ResourceRecord.Value[]>;
|
|
3
|
+
export declare namespace ResourceRecord {
|
|
4
|
+
type Value = boolean | Date | LangString | number | string;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=ResourceRecord.d.ts.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Quad } from "@rdfjs/types";
|
|
2
|
+
import type { Either } from "purify-ts";
|
|
3
|
+
import type { ResourceRecord } from "./ResourceRecord.js";
|
|
4
|
+
export interface ResourceRecordParser {
|
|
5
|
+
/**
|
|
6
|
+
* Parse a ResourceRecord into an array of RDF Quads.
|
|
7
|
+
*
|
|
8
|
+
* Receives dataFactory and other parameters here so they don't have to be passed to constructors.
|
|
9
|
+
*/
|
|
10
|
+
parse(resourceRecord: ResourceRecord): Either<Error, readonly Quad[]>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=ResourceRecordParser.d.ts.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AbstractParser } from "./AbstractParser.js";
|
|
2
|
+
import type { ResourceRecordParser } from "./ResourceRecordParser.js";
|
|
3
|
+
export type ResourceRecordParserFactory = (parameters: ConstructorParameters<typeof AbstractParser>[0]) => ResourceRecordParser;
|
|
4
|
+
//# sourceMappingURL=ResourceRecordParserFactory.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Quad } from "@rdfjs/types";
|
|
2
|
+
import { Either } from "purify-ts";
|
|
3
|
+
import { AbstractParser } from "./AbstractParser.js";
|
|
4
|
+
import type { ResourceRecordParser } from "./ResourceRecordParser.js";
|
|
5
|
+
import type { ResourceRecordParserFactory } from "./ResourceRecordParserFactory.js";
|
|
6
|
+
import type { Workbook } from "./Workbook.js";
|
|
7
|
+
export declare class ResourceWorkbookParser extends AbstractParser {
|
|
8
|
+
private readonly resourceRecordParsersByWorksheetName;
|
|
9
|
+
constructor({ resourceRecordParsersByWorksheetName, ...superParameters }: {
|
|
10
|
+
resourceRecordParsersByWorksheetName: Readonly<Record<string, ResourceRecordParser | ResourceRecordParserFactory>>;
|
|
11
|
+
} & ConstructorParameters<typeof AbstractParser>[0]);
|
|
12
|
+
parse(workbook: Workbook): Either<Error, readonly Quad[]>;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=ResourceWorkbookParser.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Either } from "purify-ts";
|
|
2
|
+
import { AbstractParser } from "./AbstractParser.js";
|
|
3
|
+
import { ResourceWorksheetParser } from "./ResourceWorksheetParser.js";
|
|
4
|
+
export class ResourceWorkbookParser extends AbstractParser {
|
|
5
|
+
resourceRecordParsersByWorksheetName;
|
|
6
|
+
constructor({ resourceRecordParsersByWorksheetName, ...superParameters }) {
|
|
7
|
+
super(superParameters);
|
|
8
|
+
this.resourceRecordParsersByWorksheetName =
|
|
9
|
+
resourceRecordParsersByWorksheetName;
|
|
10
|
+
}
|
|
11
|
+
parse(workbook) {
|
|
12
|
+
let workbookQuads = [];
|
|
13
|
+
for (const worksheet of workbook.worksheets) {
|
|
14
|
+
const resourceRecordParser = this.resourceRecordParsersByWorksheetName[worksheet.name];
|
|
15
|
+
if (resourceRecordParser === undefined) {
|
|
16
|
+
this.logger.debug({ worksheet: { name: worksheet.name } }, "no resource row parser for worksheet");
|
|
17
|
+
continue;
|
|
18
|
+
}
|
|
19
|
+
const worksheetDatasetEither = new ResourceWorksheetParser({
|
|
20
|
+
dataFactory: this.dataFactory,
|
|
21
|
+
datasetFactory: this.datasetFactory,
|
|
22
|
+
defaultNamespace: this.defaultNamespace,
|
|
23
|
+
logger: this.logger,
|
|
24
|
+
prefixMap: this.prefixMap,
|
|
25
|
+
resourceRecordParser,
|
|
26
|
+
}).parse(worksheet);
|
|
27
|
+
if (worksheetDatasetEither.isLeft()) {
|
|
28
|
+
return worksheetDatasetEither;
|
|
29
|
+
}
|
|
30
|
+
worksheetDatasetEither.ifRight((worksheetQuads) => {
|
|
31
|
+
workbookQuads = workbookQuads.concat(worksheetQuads);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return Either.of(workbookQuads);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=ResourceWorkbookParser.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Quad } from "@rdfjs/types";
|
|
2
|
+
import { Either } from "purify-ts";
|
|
3
|
+
import { AbstractParser } from "./AbstractParser.js";
|
|
4
|
+
import type { ResourceRecordParser } from "./ResourceRecordParser.js";
|
|
5
|
+
import type { ResourceRecordParserFactory } from "./ResourceRecordParserFactory.js";
|
|
6
|
+
import type { Worksheet } from "./Worksheet.js";
|
|
7
|
+
export declare class ResourceWorksheetParser extends AbstractParser {
|
|
8
|
+
private readonly resourceRecordParser;
|
|
9
|
+
constructor({ resourceRecordParser, ...superParameters }: {
|
|
10
|
+
resourceRecordParser: ResourceRecordParser | ResourceRecordParserFactory;
|
|
11
|
+
} & ConstructorParameters<typeof AbstractParser>[0]);
|
|
12
|
+
parse(worksheet: Worksheet): Either<Error, readonly Quad[]>;
|
|
13
|
+
private parseHeaderRow;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=ResourceWorksheetParser.d.ts.map
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { Either, Left, Maybe } from "purify-ts";
|
|
2
|
+
import { invariant } from "ts-invariant";
|
|
3
|
+
import { AbstractParser } from "./AbstractParser.js";
|
|
4
|
+
import { Cell } from "./Cell.js";
|
|
5
|
+
import { LangString } from "./LangString.js";
|
|
6
|
+
export class ResourceWorksheetParser extends AbstractParser {
|
|
7
|
+
resourceRecordParser;
|
|
8
|
+
constructor({ resourceRecordParser, ...superParameters }) {
|
|
9
|
+
super(superParameters);
|
|
10
|
+
this.resourceRecordParser = resourceRecordParser;
|
|
11
|
+
}
|
|
12
|
+
parse(worksheet) {
|
|
13
|
+
let headerRow;
|
|
14
|
+
const resourceRecordParser = typeof this.resourceRecordParser === "function"
|
|
15
|
+
? this.resourceRecordParser({
|
|
16
|
+
dataFactory: this.dataFactory,
|
|
17
|
+
datasetFactory: this.datasetFactory,
|
|
18
|
+
defaultNamespace: this.defaultNamespace,
|
|
19
|
+
logger: this.logger,
|
|
20
|
+
prefixMap: this.prefixMap,
|
|
21
|
+
})
|
|
22
|
+
: this.resourceRecordParser;
|
|
23
|
+
let worksheetQuads = [];
|
|
24
|
+
for (const [worksheetRowI, worksheetRow] of worksheet.rows.entries()) {
|
|
25
|
+
if (worksheetRowI === 0) {
|
|
26
|
+
const headerRowEither = this.parseHeaderRow(worksheet, worksheetRow);
|
|
27
|
+
if (headerRowEither.isLeft()) {
|
|
28
|
+
return headerRowEither.mapLeft((error) => new Error(`${worksheet.name}: ${error.message}`));
|
|
29
|
+
}
|
|
30
|
+
headerRow = headerRowEither.extract();
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
invariant(headerRow);
|
|
34
|
+
const resourceRecord = {};
|
|
35
|
+
for (const [worksheetColumnI, worksheetCell] of worksheetRow.entries()) {
|
|
36
|
+
const logContext = {
|
|
37
|
+
worksheet: {
|
|
38
|
+
column: worksheetColumnI,
|
|
39
|
+
name: worksheet.name,
|
|
40
|
+
row: worksheetRowI,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
if (worksheetColumnI >= headerRow.length) {
|
|
44
|
+
this.logger.warn(logContext, "more columns than header row");
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const columnMetadata = headerRow[worksheetColumnI];
|
|
48
|
+
if (worksheetCell === undefined) {
|
|
49
|
+
this.logger.debug(logContext, "undefined cell");
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
let newValue;
|
|
53
|
+
if (worksheetCell.hyperlink.isJust()) {
|
|
54
|
+
newValue = worksheetCell.hyperlink.extract().target;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const worksheetCellValue = worksheetCell.value.extract();
|
|
58
|
+
if (worksheetCellValue === undefined) {
|
|
59
|
+
this.logger.debug(logContext, "cell without value");
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (worksheetCellValue instanceof Cell.Error) {
|
|
63
|
+
this.logger.debug(logContext, "error cell");
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
newValue = worksheetCellValue;
|
|
67
|
+
}
|
|
68
|
+
if (columnMetadata.language.isJust()) {
|
|
69
|
+
newValue = new LangString({
|
|
70
|
+
language: columnMetadata.language.extract(),
|
|
71
|
+
value: newValue instanceof Date
|
|
72
|
+
? newValue.toISOString()
|
|
73
|
+
: newValue.toString(),
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const existingValue = resourceRecord[columnMetadata.property];
|
|
77
|
+
if (existingValue === undefined) {
|
|
78
|
+
resourceRecord[columnMetadata.property] = newValue;
|
|
79
|
+
}
|
|
80
|
+
else if (Array.isArray(existingValue)) {
|
|
81
|
+
existingValue.push(newValue);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
resourceRecord[columnMetadata.property] = [existingValue, newValue];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const rowQuadsEither = resourceRecordParser.parse(resourceRecord);
|
|
88
|
+
if (rowQuadsEither.isLeft()) {
|
|
89
|
+
return rowQuadsEither;
|
|
90
|
+
}
|
|
91
|
+
rowQuadsEither.ifRight((rowQuads) => {
|
|
92
|
+
worksheetQuads = worksheetQuads.concat(rowQuads);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return Either.of(worksheetQuads);
|
|
96
|
+
}
|
|
97
|
+
parseHeaderRow(worksheet, row) {
|
|
98
|
+
const parsed = [];
|
|
99
|
+
for (const [columnI, cell] of row.entries()) {
|
|
100
|
+
if (cell === undefined) {
|
|
101
|
+
return Left(new Error(`worksheet ${worksheet.name} header row column ${columnI} is empty`));
|
|
102
|
+
}
|
|
103
|
+
const cellValue = cell.value.extract();
|
|
104
|
+
if (cellValue === undefined) {
|
|
105
|
+
return Left(new Error(`header cell ${cell.address} has no value`));
|
|
106
|
+
}
|
|
107
|
+
if (typeof cellValue !== "string") {
|
|
108
|
+
return Left(new Error(`header cell ${cell.address} has non-string value`));
|
|
109
|
+
}
|
|
110
|
+
let property;
|
|
111
|
+
let language;
|
|
112
|
+
if (cellValue.startsWith("@")) {
|
|
113
|
+
property = cellValue;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
[property, language] = cellValue.split("@", 2);
|
|
117
|
+
}
|
|
118
|
+
property = property.trim();
|
|
119
|
+
if (property.length === 0) {
|
|
120
|
+
return Left(new Error(`header cell ${cell.address} doesn't specify a property: ${cellValue}`));
|
|
121
|
+
}
|
|
122
|
+
if (language) {
|
|
123
|
+
language = language.trim();
|
|
124
|
+
if (language.length === 0) {
|
|
125
|
+
return Left(new Error(`empty language in header cell ${cell.address}: ${cellValue}`));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
parsed.push({
|
|
129
|
+
language: Maybe.fromNullable(language),
|
|
130
|
+
property,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return Either.of(parsed);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=ResourceWorksheetParser.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Logger } from "@rdfx/logger";
|
|
2
|
+
import { Either, Maybe } from "purify-ts";
|
|
3
|
+
import type { Cell } from "./Cell.js";
|
|
4
|
+
import type { CellAddress } from "./CellAddress.js";
|
|
5
|
+
import { Worksheet } from "./Worksheet.js";
|
|
6
|
+
export declare class Workbook {
|
|
7
|
+
private readonly delegate;
|
|
8
|
+
private readonly depParser;
|
|
9
|
+
private readonly logger;
|
|
10
|
+
private constructor();
|
|
11
|
+
get cells(): readonly Cell[];
|
|
12
|
+
get cellsByAddress(): ReadonlyMap<string, Cell>;
|
|
13
|
+
/**
|
|
14
|
+
* SHA-256 sha256 hex digest of this workbook.
|
|
15
|
+
*/
|
|
16
|
+
get sha256(): string;
|
|
17
|
+
get worksheets(): readonly Worksheet[];
|
|
18
|
+
static read(data: Uint8Array, options?: {
|
|
19
|
+
logger?: Logger;
|
|
20
|
+
}): Either<Error, Workbook>;
|
|
21
|
+
cell(address: CellAddress): Maybe<Cell>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=Workbook.d.ts.map
|
package/dist/Workbook.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { nopLogger } from "@rdfx/logger";
|
|
8
|
+
import { DepParser } from "fast-formula-parser";
|
|
9
|
+
import { sha256 } from "js-sha256";
|
|
10
|
+
import { Either, Maybe } from "purify-ts";
|
|
11
|
+
import { invariant } from "ts-invariant";
|
|
12
|
+
import { Memoize } from "typescript-memoize";
|
|
13
|
+
import * as xlsx from "xlsx";
|
|
14
|
+
import { Worksheet } from "./Worksheet.js";
|
|
15
|
+
export class Workbook {
|
|
16
|
+
delegate;
|
|
17
|
+
depParser = new DepParser();
|
|
18
|
+
logger;
|
|
19
|
+
constructor({ delegate, logger, }) {
|
|
20
|
+
this.delegate = delegate;
|
|
21
|
+
this.logger = logger;
|
|
22
|
+
}
|
|
23
|
+
get cells() {
|
|
24
|
+
return [...this.cellsByAddress.values()];
|
|
25
|
+
}
|
|
26
|
+
get cellsByAddress() {
|
|
27
|
+
const cellsByAddress = new Map();
|
|
28
|
+
for (const worksheet of this.worksheets) {
|
|
29
|
+
for (const cell of worksheet.cells) {
|
|
30
|
+
invariant(!cellsByAddress.has(cell.address.string), cell.address.string);
|
|
31
|
+
cellsByAddress.set(cell.address.string, cell);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return cellsByAddress;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* SHA-256 sha256 hex digest of this workbook.
|
|
38
|
+
*/
|
|
39
|
+
get sha256() {
|
|
40
|
+
const hasher = sha256.create();
|
|
41
|
+
for (const worksheet of this.worksheets) {
|
|
42
|
+
hasher.update(worksheet.sha256);
|
|
43
|
+
}
|
|
44
|
+
return hasher.hex();
|
|
45
|
+
}
|
|
46
|
+
get worksheets() {
|
|
47
|
+
return Object.entries(this.delegate.Sheets).map(([name, worksheet], index) => new Worksheet({
|
|
48
|
+
definedNames: this.delegate.Workbook?.Names ?? [],
|
|
49
|
+
delegate: worksheet,
|
|
50
|
+
depParser: this.depParser,
|
|
51
|
+
hidden: (this.delegate.Workbook?.Sheets?.[index]?.Hidden ?? 0) !== 0,
|
|
52
|
+
index,
|
|
53
|
+
logger: this.logger,
|
|
54
|
+
name,
|
|
55
|
+
workbook: this,
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
static read(data, options) {
|
|
59
|
+
return Either.encase(() => {
|
|
60
|
+
return new Workbook({
|
|
61
|
+
delegate: xlsx.read(data, {
|
|
62
|
+
cellFormula: true,
|
|
63
|
+
cellNF: true,
|
|
64
|
+
cellDates: true,
|
|
65
|
+
sheetStubs: true,
|
|
66
|
+
WTF: true,
|
|
67
|
+
}),
|
|
68
|
+
logger: options?.logger ?? nopLogger,
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
cell(address) {
|
|
73
|
+
return Maybe.fromNullable(this.cellsByAddress.get(address.string));
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
__decorate([
|
|
77
|
+
Memoize()
|
|
78
|
+
], Workbook.prototype, "cells", null);
|
|
79
|
+
__decorate([
|
|
80
|
+
Memoize()
|
|
81
|
+
], Workbook.prototype, "cellsByAddress", null);
|
|
82
|
+
__decorate([
|
|
83
|
+
Memoize()
|
|
84
|
+
], Workbook.prototype, "sha256", null);
|
|
85
|
+
__decorate([
|
|
86
|
+
Memoize()
|
|
87
|
+
], Workbook.prototype, "worksheets", null);
|
|
88
|
+
//# sourceMappingURL=Workbook.js.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Logger } from "@rdfx/logger";
|
|
2
|
+
import type { DepParser } from "fast-formula-parser";
|
|
3
|
+
import { Maybe } from "purify-ts";
|
|
4
|
+
import * as xlsx from "xlsx";
|
|
5
|
+
import { Cell } from "./Cell.js";
|
|
6
|
+
import { CellAddress } from "./CellAddress.js";
|
|
7
|
+
import type { Workbook } from "./Workbook.js";
|
|
8
|
+
export declare class Worksheet {
|
|
9
|
+
#private;
|
|
10
|
+
private readonly delegate;
|
|
11
|
+
private readonly depParser;
|
|
12
|
+
private readonly index;
|
|
13
|
+
private readonly logger;
|
|
14
|
+
readonly hidden: boolean;
|
|
15
|
+
readonly name: string;
|
|
16
|
+
readonly workbook: Workbook;
|
|
17
|
+
constructor({ definedNames, delegate, depParser, hidden, index, logger, name, workbook, }: {
|
|
18
|
+
definedNames: readonly xlsx.DefinedName[];
|
|
19
|
+
delegate: xlsx.WorkSheet;
|
|
20
|
+
depParser: DepParser;
|
|
21
|
+
hidden: boolean;
|
|
22
|
+
index: number;
|
|
23
|
+
logger: Logger;
|
|
24
|
+
name: string;
|
|
25
|
+
workbook: Workbook;
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Array of all cells.
|
|
29
|
+
*/
|
|
30
|
+
get cells(): readonly Cell[];
|
|
31
|
+
/**
|
|
32
|
+
* Sparse map of cell address strings (qualified with sheet name: "Sheet!A1") to cells,
|
|
33
|
+
* mirroring the way .xlsx stores cells natively.
|
|
34
|
+
*/
|
|
35
|
+
get cellsByAddress(): ReadonlyMap<string, Cell>;
|
|
36
|
+
/**
|
|
37
|
+
* Get defined names from the perspective of this worksheet.
|
|
38
|
+
*
|
|
39
|
+
* The result will include:
|
|
40
|
+
* - worksheet-local names
|
|
41
|
+
* - workbook-global names that aren't overridden by a worksheet-local name
|
|
42
|
+
*/
|
|
43
|
+
get definedNames(): readonly [CellAddress, string][];
|
|
44
|
+
/**
|
|
45
|
+
* Dense array of rows with undefined's for empty cells.
|
|
46
|
+
*
|
|
47
|
+
* Includes any row that has a populated cell (i.e., within the !ref range of the worksheet).
|
|
48
|
+
*/
|
|
49
|
+
get rows(): readonly Worksheet.Row[];
|
|
50
|
+
/**
|
|
51
|
+
* SHA-256 sha256 hex digest of this worksheet.
|
|
52
|
+
*/
|
|
53
|
+
get sha256(): string;
|
|
54
|
+
cell(address: CellAddress | string): Maybe<Cell>;
|
|
55
|
+
toString(): string;
|
|
56
|
+
}
|
|
57
|
+
export declare namespace Worksheet {
|
|
58
|
+
type Row = readonly (Cell | undefined)[];
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=Worksheet.d.ts.map
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { sha256 } from "js-sha256";
|
|
8
|
+
import { Maybe } from "purify-ts";
|
|
9
|
+
import { invariant } from "ts-invariant";
|
|
10
|
+
import { Memoize } from "typescript-memoize";
|
|
11
|
+
import * as xlsx from "xlsx";
|
|
12
|
+
import { Cell } from "./Cell.js";
|
|
13
|
+
import { CellAddress } from "./CellAddress.js";
|
|
14
|
+
export class Worksheet {
|
|
15
|
+
#definedNames;
|
|
16
|
+
delegate;
|
|
17
|
+
depParser;
|
|
18
|
+
index;
|
|
19
|
+
logger;
|
|
20
|
+
hidden;
|
|
21
|
+
name;
|
|
22
|
+
workbook;
|
|
23
|
+
constructor({ definedNames, delegate, depParser, hidden, index, logger, name, workbook, }) {
|
|
24
|
+
this.#definedNames = definedNames;
|
|
25
|
+
this.delegate = delegate;
|
|
26
|
+
this.depParser = depParser;
|
|
27
|
+
this.hidden = hidden;
|
|
28
|
+
this.index = index;
|
|
29
|
+
this.logger = logger;
|
|
30
|
+
this.name = name;
|
|
31
|
+
this.workbook = workbook;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Array of all cells.
|
|
35
|
+
*/
|
|
36
|
+
get cells() {
|
|
37
|
+
return [...this.cellsByAddress.values()];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Sparse map of cell address strings (qualified with sheet name: "Sheet!A1") to cells,
|
|
41
|
+
* mirroring the way .xlsx stores cells natively.
|
|
42
|
+
*/
|
|
43
|
+
get cellsByAddress() {
|
|
44
|
+
const cellsByAddress = new Map();
|
|
45
|
+
for (const [cellAddressString, cellAny] of Object.entries(this.delegate)) {
|
|
46
|
+
// SheetJS uses keys like '!ref', '!margins' etc for metadata
|
|
47
|
+
if (cellAddressString.startsWith("!")) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const cellObject = cellAny;
|
|
51
|
+
const cellAddress = CellAddress.parse(cellAddressString, this.name);
|
|
52
|
+
const hyperlink = cellObject.l
|
|
53
|
+
? Maybe.of({
|
|
54
|
+
target: cellObject.l.Target,
|
|
55
|
+
tooltip: Maybe.fromNullable(cellObject.l.Tooltip),
|
|
56
|
+
})
|
|
57
|
+
: Maybe.empty();
|
|
58
|
+
let value;
|
|
59
|
+
switch (cellObject.t) {
|
|
60
|
+
case "b": // Boolean
|
|
61
|
+
case "d": // Date
|
|
62
|
+
case "n": // Number
|
|
63
|
+
case "s": // String
|
|
64
|
+
value = Maybe.fromNullable(cellObject.v);
|
|
65
|
+
break;
|
|
66
|
+
case "e": // Error
|
|
67
|
+
invariant(cellObject.w);
|
|
68
|
+
value = Maybe.of(new Cell.Error(cellObject.w));
|
|
69
|
+
break;
|
|
70
|
+
case "z": // Empty
|
|
71
|
+
value = Maybe.empty();
|
|
72
|
+
break;
|
|
73
|
+
default:
|
|
74
|
+
throw new RangeError(`${cellAddress}: unexpected cell object type: ${cellObject.t}`);
|
|
75
|
+
}
|
|
76
|
+
let formula;
|
|
77
|
+
if (cellObject.f === "undefined") {
|
|
78
|
+
formula = Maybe.empty();
|
|
79
|
+
value = Maybe.of(new Cell.Error("unparseable formula or macro"));
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
formula = Maybe.fromNullable(cellObject.f);
|
|
83
|
+
}
|
|
84
|
+
const cell = new Cell({
|
|
85
|
+
address: cellAddress,
|
|
86
|
+
depParser: this.depParser,
|
|
87
|
+
hyperlink,
|
|
88
|
+
formula,
|
|
89
|
+
value,
|
|
90
|
+
worksheet: this,
|
|
91
|
+
});
|
|
92
|
+
invariant(!cellsByAddress.has(cell.address.string), cell.address.string);
|
|
93
|
+
cellsByAddress.set(cell.address.string, cell);
|
|
94
|
+
}
|
|
95
|
+
return cellsByAddress;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get defined names from the perspective of this worksheet.
|
|
99
|
+
*
|
|
100
|
+
* The result will include:
|
|
101
|
+
* - worksheet-local names
|
|
102
|
+
* - workbook-global names that aren't overridden by a worksheet-local name
|
|
103
|
+
*/
|
|
104
|
+
get definedNames() {
|
|
105
|
+
const result = new Map();
|
|
106
|
+
for (const scope of ["worksheet", "workbook"]) {
|
|
107
|
+
for (const definedName of this.#definedNames) {
|
|
108
|
+
if ((scope === "worksheet" && definedName.Sheet === this.index) ||
|
|
109
|
+
(scope === "workbook" && typeof definedName.Sheet === "undefined")) {
|
|
110
|
+
const refMatch = definedName.Ref.match(/^(?:'([^']+)'|([^!]+))!\$?([A-Z]+)\$?(\d+)$/);
|
|
111
|
+
if (!refMatch) {
|
|
112
|
+
continue; // Ref is not to a single cell
|
|
113
|
+
}
|
|
114
|
+
const refWorksheetName = refMatch[1] || refMatch[2];
|
|
115
|
+
const refCol = refMatch[3];
|
|
116
|
+
const refRow = refMatch[4];
|
|
117
|
+
const refDecodedCell = xlsx.utils.decode_cell(`${refCol}${refRow}`); // { r: 0, c: 0 } (0-based)
|
|
118
|
+
const refCellAddress = new CellAddress({
|
|
119
|
+
columnIndex: refDecodedCell.c,
|
|
120
|
+
rowIndex: refDecodedCell.r,
|
|
121
|
+
worksheetName: refWorksheetName ?? this.name,
|
|
122
|
+
});
|
|
123
|
+
if (scope === "worksheet") {
|
|
124
|
+
if (result.has(definedName.Name)) {
|
|
125
|
+
this.logger.warn("duplicate worksheet-local defined name %s for cell %s", definedName.Name, refCellAddress);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
if (result.has(definedName.Name)) {
|
|
131
|
+
this.logger.debug("workbook-global name %s overridden by worksheet-local name", definedName.Name);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
result.set(definedName.Name, [refCellAddress, definedName.Name]);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return [...result.values()];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Dense array of rows with undefined's for empty cells.
|
|
143
|
+
*
|
|
144
|
+
* Includes any row that has a populated cell (i.e., within the !ref range of the worksheet).
|
|
145
|
+
*/
|
|
146
|
+
get rows() {
|
|
147
|
+
const worksheetRange = xlsx.utils.decode_range(this.delegate["!ref"] || "A1");
|
|
148
|
+
const rows = [];
|
|
149
|
+
for (let r = worksheetRange.s.r; r <= worksheetRange.e.r; ++r) {
|
|
150
|
+
const row = [];
|
|
151
|
+
for (let c = worksheetRange.s.c; c <= worksheetRange.e.c; ++c) {
|
|
152
|
+
row.push(this.cellsByAddress.get(CellAddress.parse(xlsx.utils.encode_cell({ c, r }), this.name)
|
|
153
|
+
.string));
|
|
154
|
+
}
|
|
155
|
+
rows.push(row);
|
|
156
|
+
}
|
|
157
|
+
return rows;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* SHA-256 sha256 hex digest of this worksheet.
|
|
161
|
+
*/
|
|
162
|
+
get sha256() {
|
|
163
|
+
const hasher = sha256.create();
|
|
164
|
+
for (const cell of this.cells) {
|
|
165
|
+
hasher.update(cell.sha256);
|
|
166
|
+
}
|
|
167
|
+
return hasher.hex();
|
|
168
|
+
}
|
|
169
|
+
cell(address) {
|
|
170
|
+
return Maybe.fromNullable(this.cellsByAddress.get(typeof address === "object"
|
|
171
|
+
? address.string
|
|
172
|
+
: CellAddress.parse(address, this.name).string));
|
|
173
|
+
}
|
|
174
|
+
toString() {
|
|
175
|
+
return `Worksheet(name=${this.name})`;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
__decorate([
|
|
179
|
+
Memoize()
|
|
180
|
+
], Worksheet.prototype, "cells", null);
|
|
181
|
+
__decorate([
|
|
182
|
+
Memoize()
|
|
183
|
+
], Worksheet.prototype, "cellsByAddress", null);
|
|
184
|
+
__decorate([
|
|
185
|
+
Memoize()
|
|
186
|
+
], Worksheet.prototype, "definedNames", null);
|
|
187
|
+
__decorate([
|
|
188
|
+
Memoize()
|
|
189
|
+
], Worksheet.prototype, "rows", null);
|
|
190
|
+
__decorate([
|
|
191
|
+
Memoize()
|
|
192
|
+
], Worksheet.prototype, "sha256", null);
|
|
193
|
+
__decorate([
|
|
194
|
+
Memoize()
|
|
195
|
+
], Worksheet.prototype, "toString", null);
|
|
196
|
+
//# sourceMappingURL=Worksheet.js.map
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
{
|
|
2
|
+
"dependencies": {
|
|
3
|
+
"@rdfx/logger": "0.0.60",
|
|
4
|
+
"@rdfx/resource": "0.0.60",
|
|
5
|
+
"@rdfjs/namespace": "~2.0.1",
|
|
6
|
+
"@rdfjs/prefix-map": "~0.1.2",
|
|
7
|
+
"@rdfjs/types": "~2.0.1",
|
|
8
|
+
"@types/rdfjs__namespace": "~2.0.10",
|
|
9
|
+
"@types/rdfjs__prefix-map": "~0.1.5",
|
|
10
|
+
"fast-formula-parser": "1.0.19",
|
|
11
|
+
"js-sha256": "~0.11.1",
|
|
12
|
+
"purify-ts": "~2.1.4",
|
|
13
|
+
"ts-invariant": "~0.10.3",
|
|
14
|
+
"typescript-memoize": "~1.1.1",
|
|
15
|
+
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@rdfx/collection": "0.0.60",
|
|
19
|
+
"@rdfx/testing": "0.0.60",
|
|
20
|
+
"@shaclmate/cli": "4.0.100",
|
|
21
|
+
"@types/node": "^24",
|
|
22
|
+
"libreoffice-convert": "~1.8.1",
|
|
23
|
+
"zod": "~4.1.12"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"LICENSE",
|
|
27
|
+
"dist/*.d.ts",
|
|
28
|
+
"dist/*.js"
|
|
29
|
+
],
|
|
30
|
+
"license": "Apache-2.0",
|
|
31
|
+
"main": "./dist/index.js",
|
|
2
32
|
"name": "@rdfx/xlsx",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
|
|
6
|
-
|
|
33
|
+
"packageManager": "npm@11.11.0",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/minorg/rdfx.git"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -b",
|
|
40
|
+
"clean": "rimraf dist",
|
|
41
|
+
"depcheck": "depcheck --ignores=\"@tsconfig/*,vitest,vitest-fetch-mock\" .",
|
|
42
|
+
"dev": "tsc -w --preserveWatchOutput",
|
|
43
|
+
"dev:tests": "tsc -p __tests__ -w --preserveWatchOutput",
|
|
44
|
+
"test": "cd ../.. && vitest run packages/xlsx/__tests__"
|
|
45
|
+
},
|
|
46
|
+
"type": "module",
|
|
47
|
+
"types": "./dist/index.d.ts",
|
|
48
|
+
"version": "0.0.60"
|
|
49
|
+
}
|
package/README.md
DELETED