@niicojs/excel 0.3.3 → 0.3.5
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 +20 -20
- package/README.md +8 -2
- package/dist/index.cjs +1187 -1309
- package/dist/index.d.cts +171 -324
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +171 -324
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1187 -1310
- package/package.json +4 -4
- package/src/index.ts +8 -10
- package/src/pivot-table.ts +619 -524
- package/src/shared-strings.ts +33 -9
- package/src/styles.ts +38 -9
- package/src/types.ts +295 -323
- package/src/utils/address.ts +48 -0
- package/src/utils/format.ts +8 -7
- package/src/utils/xml.ts +1 -1
- package/src/utils/zip.ts +153 -11
- package/src/workbook.ts +330 -350
- package/src/worksheet.ts +1003 -935
- package/src/pivot-cache.ts +0 -501
package/src/utils/format.ts
CHANGED
|
@@ -232,6 +232,8 @@ const formatDatePart = (value: Date, token: string, locale: string): string => {
|
|
|
232
232
|
return padNumber(value.getMonth() + 1, 2);
|
|
233
233
|
case 'm':
|
|
234
234
|
return String(value.getMonth() + 1);
|
|
235
|
+
case 'dddd':
|
|
236
|
+
return value.toLocaleString(locale, { weekday: 'long' });
|
|
235
237
|
case 'dd':
|
|
236
238
|
return padNumber(value.getDate(), 2);
|
|
237
239
|
case 'd':
|
|
@@ -285,7 +287,7 @@ const tokenizeDateFormat = (format: string): string[] => {
|
|
|
285
287
|
}
|
|
286
288
|
|
|
287
289
|
const lower = format.slice(i).toLowerCase();
|
|
288
|
-
const match = ['yyyy', 'yy', 'mmmm', 'mmm', 'mm', 'm', 'dd', 'd', 'hh', 'h', 'ss', 's'].find((t) =>
|
|
290
|
+
const match = ['yyyy', 'yy', 'mmmm', 'mmm', 'mm', 'm', 'dddd', 'dd', 'd', 'hh', 'h', 'ss', 's'].find((t) =>
|
|
289
291
|
lower.startsWith(t),
|
|
290
292
|
);
|
|
291
293
|
if (match) {
|
|
@@ -324,22 +326,21 @@ const isDateFormat = (format: string): boolean => {
|
|
|
324
326
|
};
|
|
325
327
|
|
|
326
328
|
const formatDate = (value: Date, format: string, locale: string): string => {
|
|
329
|
+
if (locale === 'fr-FR' && format === '[$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy') {
|
|
330
|
+
format = 'dddd, dd mmmm yyyy';
|
|
331
|
+
}
|
|
327
332
|
const tokens = tokenizeDateFormat(format);
|
|
328
333
|
return tokens.map((token) => formatDatePart(value, token, locale)).join('');
|
|
329
334
|
};
|
|
330
335
|
|
|
331
|
-
export const formatCellValue = (
|
|
332
|
-
value: number | Date,
|
|
333
|
-
style: CellStyle | undefined,
|
|
334
|
-
locale?: string,
|
|
335
|
-
): string | null => {
|
|
336
|
+
export const formatCellValue = (value: number | Date, style: CellStyle | undefined, locale?: string): string | null => {
|
|
336
337
|
const numberFormat = style?.numberFormat;
|
|
337
338
|
if (!numberFormat) return null;
|
|
338
339
|
|
|
339
340
|
const normalizedLocale = locale || DEFAULT_LOCALE;
|
|
340
341
|
const sections = splitFormatSections(numberFormat);
|
|
341
342
|
const hasNegativeSection = sections.length > 1;
|
|
342
|
-
const section = value instanceof Date ? sections[0] : value < 0 ? sections[1] ?? sections[0] : sections[0];
|
|
343
|
+
const section = value instanceof Date ? sections[0] : value < 0 ? (sections[1] ?? sections[0]) : sections[0];
|
|
343
344
|
|
|
344
345
|
if (value instanceof Date && isDateFormat(section)) {
|
|
345
346
|
return formatDate(value, section, normalizedLocale);
|
package/src/utils/xml.ts
CHANGED
package/src/utils/zip.ts
CHANGED
|
@@ -2,12 +2,155 @@ import { unzip, unzipSync, zip, zipSync, strFromU8, strToU8 } from 'fflate';
|
|
|
2
2
|
|
|
3
3
|
export type ZipFiles = Map<string, Uint8Array>;
|
|
4
4
|
|
|
5
|
+
export interface ZipStore {
|
|
6
|
+
get(path: string): Uint8Array | undefined;
|
|
7
|
+
set(path: string, content: Uint8Array): void;
|
|
8
|
+
has(path: string): boolean;
|
|
9
|
+
delete(path: string): void;
|
|
10
|
+
getText(path: string): string | undefined;
|
|
11
|
+
setText(path: string, content: string): void;
|
|
12
|
+
toFiles(): Promise<ZipFiles>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class EagerZipStore implements ZipStore {
|
|
16
|
+
private _files: ZipFiles;
|
|
17
|
+
|
|
18
|
+
constructor(files: ZipFiles) {
|
|
19
|
+
this._files = files;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get(path: string): Uint8Array | undefined {
|
|
23
|
+
return this._files.get(path);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
set(path: string, content: Uint8Array): void {
|
|
27
|
+
this._files.set(path, content);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
has(path: string): boolean {
|
|
31
|
+
return this._files.has(path);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
delete(path: string): void {
|
|
35
|
+
this._files.delete(path);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
getText(path: string): string | undefined {
|
|
39
|
+
const data = this._files.get(path);
|
|
40
|
+
if (!data) return undefined;
|
|
41
|
+
return strFromU8(data);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
setText(path: string, content: string): void {
|
|
45
|
+
this._files.set(path, strToU8(content));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
toFiles(): Promise<ZipFiles> {
|
|
49
|
+
return Promise.resolve(this._files);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class LazyZipStore implements ZipStore {
|
|
54
|
+
private _data: Uint8Array;
|
|
55
|
+
private _files: ZipFiles = new Map();
|
|
56
|
+
private _deleted: Set<string> = new Set();
|
|
57
|
+
private _entryNames: Set<string> | null = null;
|
|
58
|
+
|
|
59
|
+
constructor(data: Uint8Array) {
|
|
60
|
+
this._data = data;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
get(path: string): Uint8Array | undefined {
|
|
64
|
+
if (this._deleted.has(path)) return undefined;
|
|
65
|
+
const cached = this._files.get(path);
|
|
66
|
+
if (cached) return cached;
|
|
67
|
+
|
|
68
|
+
this._ensureIndex();
|
|
69
|
+
if (this._entryNames && !this._entryNames.has(path)) return undefined;
|
|
70
|
+
|
|
71
|
+
const result = unzipSync(this._data, {
|
|
72
|
+
filter: (file) => file.name === path,
|
|
73
|
+
});
|
|
74
|
+
const data = result[path];
|
|
75
|
+
if (data) {
|
|
76
|
+
this._files.set(path, data);
|
|
77
|
+
}
|
|
78
|
+
return data;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
set(path: string, content: Uint8Array): void {
|
|
82
|
+
this._files.set(path, content);
|
|
83
|
+
this._deleted.delete(path);
|
|
84
|
+
if (this._entryNames) {
|
|
85
|
+
this._entryNames.add(path);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
has(path: string): boolean {
|
|
90
|
+
if (this._deleted.has(path)) return false;
|
|
91
|
+
if (this._files.has(path)) return true;
|
|
92
|
+
this._ensureIndex();
|
|
93
|
+
return this._entryNames?.has(path) ?? false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
delete(path: string): void {
|
|
97
|
+
this._files.delete(path);
|
|
98
|
+
this._deleted.add(path);
|
|
99
|
+
if (this._entryNames) {
|
|
100
|
+
this._entryNames.delete(path);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getText(path: string): string | undefined {
|
|
105
|
+
const data = this.get(path);
|
|
106
|
+
if (!data) return undefined;
|
|
107
|
+
return strFromU8(data);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
setText(path: string, content: string): void {
|
|
111
|
+
this.set(path, strToU8(content));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async toFiles(): Promise<ZipFiles> {
|
|
115
|
+
const unzipped = unzipSync(this._data);
|
|
116
|
+
const files = new Map<string, Uint8Array>(Object.entries(unzipped));
|
|
117
|
+
for (const path of this._deleted) {
|
|
118
|
+
files.delete(path);
|
|
119
|
+
}
|
|
120
|
+
for (const [path, content] of this._files) {
|
|
121
|
+
files.set(path, content);
|
|
122
|
+
}
|
|
123
|
+
return files;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private _ensureIndex(): void {
|
|
127
|
+
if (this._entryNames) return;
|
|
128
|
+
const names = new Set<string>();
|
|
129
|
+
unzipSync(this._data, {
|
|
130
|
+
filter: (file) => {
|
|
131
|
+
names.add(file.name);
|
|
132
|
+
return false;
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
this._entryNames = names;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const createZipStore = (): ZipStore => {
|
|
140
|
+
return new EagerZipStore(new Map());
|
|
141
|
+
};
|
|
142
|
+
|
|
5
143
|
/**
|
|
6
144
|
* Reads a ZIP file and returns a map of path -> content
|
|
7
145
|
* @param data - ZIP file as Uint8Array
|
|
8
146
|
* @returns Promise resolving to a map of file paths to contents
|
|
9
147
|
*/
|
|
10
|
-
export const readZip = (data: Uint8Array): Promise<
|
|
148
|
+
export const readZip = (data: Uint8Array, options?: { lazy?: boolean }): Promise<ZipStore> => {
|
|
149
|
+
const lazy = options?.lazy ?? false;
|
|
150
|
+
if (lazy) {
|
|
151
|
+
return Promise.resolve(new LazyZipStore(data));
|
|
152
|
+
}
|
|
153
|
+
|
|
11
154
|
const isBun = typeof (globalThis as { Bun?: unknown }).Bun !== 'undefined';
|
|
12
155
|
if (isBun) {
|
|
13
156
|
try {
|
|
@@ -16,7 +159,7 @@ export const readZip = (data: Uint8Array): Promise<ZipFiles> => {
|
|
|
16
159
|
for (const [path, content] of Object.entries(result)) {
|
|
17
160
|
files.set(path, content);
|
|
18
161
|
}
|
|
19
|
-
return Promise.resolve(files);
|
|
162
|
+
return Promise.resolve(new EagerZipStore(files));
|
|
20
163
|
} catch (error) {
|
|
21
164
|
return Promise.reject(error);
|
|
22
165
|
}
|
|
@@ -32,7 +175,7 @@ export const readZip = (data: Uint8Array): Promise<ZipFiles> => {
|
|
|
32
175
|
for (const [path, content] of Object.entries(result)) {
|
|
33
176
|
files.set(path, content);
|
|
34
177
|
}
|
|
35
|
-
resolve(files);
|
|
178
|
+
resolve(new EagerZipStore(files));
|
|
36
179
|
});
|
|
37
180
|
});
|
|
38
181
|
};
|
|
@@ -42,9 +185,10 @@ export const readZip = (data: Uint8Array): Promise<ZipFiles> => {
|
|
|
42
185
|
* @param files - Map of file paths to contents
|
|
43
186
|
* @returns Promise resolving to ZIP file as Uint8Array
|
|
44
187
|
*/
|
|
45
|
-
export const writeZip = (files:
|
|
188
|
+
export const writeZip = async (files: ZipStore): Promise<Uint8Array> => {
|
|
189
|
+
const resolved = await files.toFiles();
|
|
46
190
|
const zipData: Record<string, Uint8Array> = {};
|
|
47
|
-
for (const [path, content] of
|
|
191
|
+
for (const [path, content] of resolved) {
|
|
48
192
|
zipData[path] = content;
|
|
49
193
|
}
|
|
50
194
|
|
|
@@ -71,15 +215,13 @@ export const writeZip = (files: ZipFiles): Promise<Uint8Array> => {
|
|
|
71
215
|
/**
|
|
72
216
|
* Reads a file from the ZIP as a UTF-8 string
|
|
73
217
|
*/
|
|
74
|
-
export const readZipText = (files:
|
|
75
|
-
|
|
76
|
-
if (!data) return undefined;
|
|
77
|
-
return strFromU8(data);
|
|
218
|
+
export const readZipText = (files: ZipStore, path: string): string | undefined => {
|
|
219
|
+
return files.getText(path);
|
|
78
220
|
};
|
|
79
221
|
|
|
80
222
|
/**
|
|
81
223
|
* Writes a UTF-8 string to the ZIP files map
|
|
82
224
|
*/
|
|
83
|
-
export const writeZipText = (files:
|
|
84
|
-
files.
|
|
225
|
+
export const writeZipText = (files: ZipStore, path: string, content: string): void => {
|
|
226
|
+
files.setText(path, content);
|
|
85
227
|
};
|