@cj-tech-master/excelts 7.6.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -577
- package/README_zh.md +101 -577
- package/dist/browser/index.browser.d.ts +3 -0
- package/dist/browser/index.browser.js +2 -0
- package/dist/browser/index.d.ts +3 -0
- package/dist/browser/index.js +2 -0
- package/dist/browser/modules/archive/compression/compress.browser.js +4 -4
- package/dist/browser/modules/archive/compression/deflate-fallback.d.ts +24 -22
- package/dist/browser/modules/archive/compression/deflate-fallback.js +664 -360
- package/dist/browser/modules/archive/compression/streaming-compress.browser.d.ts +7 -0
- package/dist/browser/modules/archive/compression/streaming-compress.browser.js +15 -3
- package/dist/browser/modules/archive/compression/streaming-compress.d.ts +5 -0
- package/dist/browser/modules/archive/compression/streaming-compress.js +7 -0
- package/dist/browser/modules/archive/zip/stream.js +27 -3
- package/dist/browser/modules/excel/workbook.browser.d.ts +72 -0
- package/dist/browser/modules/excel/workbook.browser.js +226 -0
- package/dist/browser/modules/excel/workbook.d.ts +32 -1
- package/dist/browser/modules/excel/workbook.js +47 -2
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +42 -4
- package/dist/browser/modules/markdown/constants.d.ts +30 -0
- package/dist/browser/modules/markdown/constants.js +30 -0
- package/dist/browser/modules/markdown/errors.d.ts +21 -0
- package/dist/browser/modules/markdown/errors.js +23 -0
- package/dist/browser/modules/markdown/format/index.d.ts +54 -0
- package/dist/browser/modules/markdown/format/index.js +307 -0
- package/dist/browser/modules/markdown/index.d.ts +15 -0
- package/dist/browser/modules/markdown/index.js +22 -0
- package/dist/browser/modules/markdown/parse/index.d.ts +70 -0
- package/dist/browser/modules/markdown/parse/index.js +428 -0
- package/dist/browser/modules/markdown/types.d.ts +130 -0
- package/dist/browser/modules/markdown/types.js +6 -0
- package/dist/cjs/index.js +5 -1
- package/dist/cjs/modules/archive/compression/compress.browser.js +4 -4
- package/dist/cjs/modules/archive/compression/deflate-fallback.js +664 -360
- package/dist/cjs/modules/archive/compression/streaming-compress.browser.js +15 -2
- package/dist/cjs/modules/archive/compression/streaming-compress.js +8 -0
- package/dist/cjs/modules/archive/zip/stream.js +26 -2
- package/dist/cjs/modules/excel/workbook.browser.js +226 -0
- package/dist/cjs/modules/excel/workbook.js +46 -1
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +42 -4
- package/dist/cjs/modules/markdown/constants.js +33 -0
- package/dist/cjs/modules/markdown/errors.js +28 -0
- package/dist/cjs/modules/markdown/format/index.js +310 -0
- package/dist/cjs/modules/markdown/index.js +30 -0
- package/dist/cjs/modules/markdown/parse/index.js +432 -0
- package/dist/cjs/modules/markdown/types.js +7 -0
- package/dist/esm/index.browser.js +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/modules/archive/compression/compress.browser.js +4 -4
- package/dist/esm/modules/archive/compression/deflate-fallback.js +664 -360
- package/dist/esm/modules/archive/compression/streaming-compress.browser.js +15 -3
- package/dist/esm/modules/archive/compression/streaming-compress.js +7 -0
- package/dist/esm/modules/archive/zip/stream.js +27 -3
- package/dist/esm/modules/excel/workbook.browser.js +226 -0
- package/dist/esm/modules/excel/workbook.js +47 -2
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +42 -4
- package/dist/esm/modules/markdown/constants.js +30 -0
- package/dist/esm/modules/markdown/errors.js +23 -0
- package/dist/esm/modules/markdown/format/index.js +307 -0
- package/dist/esm/modules/markdown/index.js +22 -0
- package/dist/esm/modules/markdown/parse/index.js +428 -0
- package/dist/esm/modules/markdown/types.js +6 -0
- package/dist/iife/excelts.iife.js +1342 -283
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +38 -34
- package/dist/types/index.browser.d.ts +3 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/modules/archive/compression/deflate-fallback.d.ts +24 -22
- package/dist/types/modules/archive/compression/streaming-compress.browser.d.ts +7 -0
- package/dist/types/modules/archive/compression/streaming-compress.d.ts +5 -0
- package/dist/types/modules/excel/workbook.browser.d.ts +72 -0
- package/dist/types/modules/excel/workbook.d.ts +32 -1
- package/dist/types/modules/markdown/constants.d.ts +30 -0
- package/dist/types/modules/markdown/errors.d.ts +21 -0
- package/dist/types/modules/markdown/format/index.d.ts +54 -0
- package/dist/types/modules/markdown/index.d.ts +15 -0
- package/dist/types/modules/markdown/parse/index.d.ts +70 -0
- package/dist/types/modules/markdown/types.d.ts +130 -0
- package/package.json +56 -32
|
@@ -44,3 +44,10 @@ export declare function createUnzlibStream(options?: StreamCompressOptions): Unz
|
|
|
44
44
|
* bit-stream state across `write()` calls.
|
|
45
45
|
*/
|
|
46
46
|
export { PureJsSyncDeflater as SyncDeflater };
|
|
47
|
+
/**
|
|
48
|
+
* Returns true when the browser supports native `CompressionStream("deflate-raw")`,
|
|
49
|
+
* signalling that `push()` should prefer the async path over `SyncDeflater`.
|
|
50
|
+
*
|
|
51
|
+
* Only checks for compression support — decompression is not needed for writing.
|
|
52
|
+
*/
|
|
53
|
+
export declare function hasNativeAsyncDeflate(): boolean;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { EventEmitter } from "../../../utils/event-emitter.js";
|
|
13
13
|
import { deflateRawCompressed, inflateRaw, SyncDeflater as PureJsSyncDeflater } from "./deflate-fallback.js";
|
|
14
|
-
import { hasDeflateRawWebStreams, hasGzipCompressionStream, hasGzipDecompressionStream, hasDeflateCompressionStream, hasDeflateDecompressionStream } from "./compress.base.js";
|
|
14
|
+
import { hasDeflateRawWebStreams, hasDeflateRawCompressionStream, hasGzipCompressionStream, hasGzipDecompressionStream, hasDeflateCompressionStream, hasDeflateDecompressionStream } from "./compress.base.js";
|
|
15
15
|
import { concatUint8Arrays } from "../../../utils/binary.js";
|
|
16
16
|
import { DEFAULT_COMPRESS_LEVEL } from "../shared/defaults.js";
|
|
17
17
|
import { hasWorkerSupport, getDefaultWorkerPool } from "./worker-pool/index.browser.js";
|
|
@@ -253,10 +253,13 @@ function createStreamCodec(type, options) {
|
|
|
253
253
|
if (options.useWorker && hasWorkerSupport()) {
|
|
254
254
|
return createWorkerStreamCodec(type, options.workerPool, level, options.allowTransfer);
|
|
255
255
|
}
|
|
256
|
-
|
|
256
|
+
// Use native CompressionStream/DecompressionStream when the required
|
|
257
|
+
// direction is available. Compression only needs CompressionStream;
|
|
258
|
+
// decompression only needs DecompressionStream.
|
|
259
|
+
if (type === "deflate" ? hasDeflateRawCompressionStream() : hasDeflateRawWebStreams()) {
|
|
257
260
|
return createNativeWebStreamCodec("deflate-raw", type === "deflate");
|
|
258
261
|
}
|
|
259
|
-
return new BufferedCodec(type === "deflate" ? deflateRawCompressed : inflateRaw);
|
|
262
|
+
return new BufferedCodec(type === "deflate" ? data => deflateRawCompressed(data, level) : inflateRaw);
|
|
260
263
|
}
|
|
261
264
|
// =============================================================================
|
|
262
265
|
// Public API
|
|
@@ -317,3 +320,12 @@ export function createUnzlibStream(options = {}) {
|
|
|
317
320
|
* bit-stream state across `write()` calls.
|
|
318
321
|
*/
|
|
319
322
|
export { PureJsSyncDeflater as SyncDeflater };
|
|
323
|
+
/**
|
|
324
|
+
* Returns true when the browser supports native `CompressionStream("deflate-raw")`,
|
|
325
|
+
* signalling that `push()` should prefer the async path over `SyncDeflater`.
|
|
326
|
+
*
|
|
327
|
+
* Only checks for compression support — decompression is not needed for writing.
|
|
328
|
+
*/
|
|
329
|
+
export function hasNativeAsyncDeflate() {
|
|
330
|
+
return hasDeflateRawCompressionStream();
|
|
331
|
+
}
|
|
@@ -75,3 +75,8 @@ export declare class SyncDeflater implements SyncDeflaterLike {
|
|
|
75
75
|
finish(): Uint8Array;
|
|
76
76
|
private _flushBatch;
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* On Node.js, `zlib.deflateRawSync` is native and fast — no need to detour
|
|
80
|
+
* through the async streaming path. Always returns false.
|
|
81
|
+
*/
|
|
82
|
+
export declare function hasNativeAsyncDeflate(): boolean;
|
|
@@ -166,3 +166,10 @@ export class SyncDeflater {
|
|
|
166
166
|
return new Uint8Array(result);
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* On Node.js, `zlib.deflateRawSync` is native and fast — no need to detour
|
|
171
|
+
* through the async streaming path. Always returns false.
|
|
172
|
+
*/
|
|
173
|
+
export function hasNativeAsyncDeflate() {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - In browser builds the bundler aliases those imports to their browser variants.
|
|
7
7
|
*/
|
|
8
8
|
import { crc32Update, crc32Finalize, ensureZlibSync } from "../compression/crc32.browser.js";
|
|
9
|
-
import { createDeflateStream, SyncDeflater } from "../compression/streaming-compress.browser.js";
|
|
9
|
+
import { createDeflateStream, SyncDeflater, hasNativeAsyncDeflate } from "../compression/streaming-compress.browser.js";
|
|
10
10
|
import { zipCryptoInitKeys, zipCryptoCreateHeader, zipCryptoEncryptByte, aesEncrypt, buildAesExtraField, randomBytes, isAesEncryption, getAesKeyStrength } from "../crypto/index.js";
|
|
11
11
|
import { DEFAULT_ZIP_LEVEL, DEFAULT_ZIP_TIMESTAMPS } from "../shared/defaults.js";
|
|
12
12
|
import { EMPTY_UINT8ARRAY } from "../shared/bytes.js";
|
|
@@ -571,6 +571,12 @@ export class ZipDeflateFile {
|
|
|
571
571
|
this._tapCallback(promise, callback);
|
|
572
572
|
return promise;
|
|
573
573
|
}
|
|
574
|
+
// If a previous async operation already failed, don't do more work.
|
|
575
|
+
if (this._completeError) {
|
|
576
|
+
const promise = Promise.reject(this._completeError);
|
|
577
|
+
this._tapCallback(promise, callback);
|
|
578
|
+
return promise;
|
|
579
|
+
}
|
|
574
580
|
if (this._deflateWanted === null) {
|
|
575
581
|
this._accumulateSampleLen(data);
|
|
576
582
|
if (!this._shouldDecide(final)) {
|
|
@@ -613,7 +619,16 @@ export class ZipDeflateFile {
|
|
|
613
619
|
* memory growth when callers push data in a tight synchronous loop.
|
|
614
620
|
*/
|
|
615
621
|
push(data, final = false, callback) {
|
|
616
|
-
|
|
622
|
+
// Use the synchronous path only when:
|
|
623
|
+
// 1. No async deflate stream is already active
|
|
624
|
+
// 2. No encryption (which requires async crypto)
|
|
625
|
+
// 3. No native async deflate available (browser CompressionStream)
|
|
626
|
+
//
|
|
627
|
+
// When a native async CompressionStream("deflate-raw") is available
|
|
628
|
+
// (modern browsers), prefer the async path — it produces better
|
|
629
|
+
// compression (Dynamic Huffman via the browser's native engine) and
|
|
630
|
+
// doesn't block the main thread.
|
|
631
|
+
if (!this._deflate && this._encryptionMethod === "none" && !hasNativeAsyncDeflate()) {
|
|
617
632
|
try {
|
|
618
633
|
this._pushSyncPath(data, final);
|
|
619
634
|
callback?.();
|
|
@@ -624,7 +639,10 @@ export class ZipDeflateFile {
|
|
|
624
639
|
}
|
|
625
640
|
return Promise.resolve();
|
|
626
641
|
}
|
|
627
|
-
|
|
642
|
+
// Chain the async push so calls are serialized. Use a recovery wrapper
|
|
643
|
+
// so that a single failed push does not break the chain for subsequent
|
|
644
|
+
// pushes — errors are surfaced via onerror/rejectComplete instead.
|
|
645
|
+
const promise = (this._pushChain = this._pushChain.then(() => this._pushUnchained(data, final, callback), () => this._pushUnchained(data, final, callback)));
|
|
628
646
|
// Prevent unhandled rejection when callers intentionally ignore the Promise.
|
|
629
647
|
promise.catch(() => { });
|
|
630
648
|
return promise;
|
|
@@ -695,6 +713,12 @@ export class ZipDeflateFile {
|
|
|
695
713
|
this._syncDeflater = null;
|
|
696
714
|
}
|
|
697
715
|
this._emitDataDescriptor();
|
|
716
|
+
// _emitDataDescriptor may call _rejectComplete instead of throwing
|
|
717
|
+
// (e.g. ZIP64 required but zip64=false). In the sync path, surface
|
|
718
|
+
// this as a throw so push() can reject properly.
|
|
719
|
+
if (this._completeError) {
|
|
720
|
+
throw this._completeError;
|
|
721
|
+
}
|
|
698
722
|
}
|
|
699
723
|
/**
|
|
700
724
|
* Emit local file header with Data Descriptor flag
|
|
@@ -16,6 +16,7 @@ import { WorkbookWriter, type WorkbookWriterOptions } from "./stream/workbook-wr
|
|
|
16
16
|
import { WorkbookReader, type WorkbookReaderOptions } from "./stream/workbook-reader.js";
|
|
17
17
|
import { type DateFormat } from "../../utils/datetime.js";
|
|
18
18
|
import type { CsvParseOptions, CsvFormatOptions } from "../csv/types.js";
|
|
19
|
+
import type { MarkdownOptions } from "../markdown/types.js";
|
|
19
20
|
import type { Readable } from "../stream/index.js";
|
|
20
21
|
import type { IReadable, IWritable } from "../stream/types.js";
|
|
21
22
|
import type { PivotTable } from "./pivot-table.js";
|
|
@@ -253,6 +254,77 @@ declare class Workbook {
|
|
|
253
254
|
private _readCsvFile;
|
|
254
255
|
private _readCsvBlob;
|
|
255
256
|
private _writeCsvString;
|
|
257
|
+
/**
|
|
258
|
+
* Populate a worksheet from a parsed Markdown table result.
|
|
259
|
+
* Shared by readMarkdown and readMarkdownAll.
|
|
260
|
+
*/
|
|
261
|
+
private _populateMarkdownWorksheet;
|
|
262
|
+
/**
|
|
263
|
+
* Read a Markdown table and add as worksheet.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```ts
|
|
267
|
+
* // From a Markdown string
|
|
268
|
+
* workbook.readMarkdown("| Name | Age |\n| --- | --- |\n| Alice | 30 |");
|
|
269
|
+
*
|
|
270
|
+
* // With options
|
|
271
|
+
* workbook.readMarkdown(markdownString, { sheetName: "Data", map: (v, col) => Number(v) || v });
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
readMarkdown(input: string, options?: MarkdownOptions): Worksheet;
|
|
275
|
+
/**
|
|
276
|
+
* Read all Markdown tables from a document, each becoming a separate worksheet.
|
|
277
|
+
*
|
|
278
|
+
* @param input - Markdown string containing one or more tables
|
|
279
|
+
* @param options - Parse options (sheetName is used as prefix: "sheetName", "sheetName_2", ...)
|
|
280
|
+
* @returns Array of created worksheets (empty if no tables found)
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* ```ts
|
|
284
|
+
* // Parse a document with multiple tables
|
|
285
|
+
* const sheets = workbook.readMarkdownAll(markdownDoc);
|
|
286
|
+
* console.log(`Created ${sheets.length} worksheets`);
|
|
287
|
+
*
|
|
288
|
+
* // With a naming prefix
|
|
289
|
+
* const sheets = workbook.readMarkdownAll(markdownDoc, { sheetName: "Table" });
|
|
290
|
+
* // Creates "Table", "Table_2", "Table_3", ...
|
|
291
|
+
* ```
|
|
292
|
+
*/
|
|
293
|
+
readMarkdownAll(input: string, options?: MarkdownOptions): Worksheet[];
|
|
294
|
+
/**
|
|
295
|
+
* Write worksheet as a Markdown table string.
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* ```ts
|
|
299
|
+
* // Write first worksheet
|
|
300
|
+
* const markdownText = workbook.writeMarkdown();
|
|
301
|
+
*
|
|
302
|
+
* // Write specific worksheet with options
|
|
303
|
+
* const markdownText = workbook.writeMarkdown({ sheetName: "Data", padding: true });
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
306
|
+
writeMarkdown(options?: MarkdownOptions): string;
|
|
307
|
+
/**
|
|
308
|
+
* Write worksheet to Markdown buffer (Uint8Array).
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```ts
|
|
312
|
+
* const buffer = workbook.writeMarkdownBuffer();
|
|
313
|
+
* ```
|
|
314
|
+
*/
|
|
315
|
+
writeMarkdownBuffer(options?: MarkdownOptions): Uint8Array;
|
|
316
|
+
/**
|
|
317
|
+
* Read Markdown from file (Node.js only - throws in browser)
|
|
318
|
+
*/
|
|
319
|
+
readMarkdownFile(_filename: string, _options?: MarkdownOptions): Promise<Worksheet>;
|
|
320
|
+
/**
|
|
321
|
+
* Read all Markdown tables from file (Node.js only - throws in browser)
|
|
322
|
+
*/
|
|
323
|
+
readMarkdownAllFile(_filename: string, _options?: MarkdownOptions): Promise<Worksheet[]>;
|
|
324
|
+
/**
|
|
325
|
+
* Write Markdown to file (Node.js only - throws in browser)
|
|
326
|
+
*/
|
|
327
|
+
writeMarkdownFile(_filename: string, _options?: MarkdownOptions): Promise<void>;
|
|
256
328
|
/**
|
|
257
329
|
* Create a streaming workbook writer for large files.
|
|
258
330
|
* This is more memory-efficient than using Workbook for large datasets.
|
|
@@ -19,6 +19,8 @@ import { parseCsv } from "../csv/parse/index.js";
|
|
|
19
19
|
import { formatCsv } from "../csv/format/index.js";
|
|
20
20
|
import { CsvParserStream, CsvFormatterStream } from "../csv/stream/index.js";
|
|
21
21
|
import { parseNumberFromCsv } from "../csv/utils/number.js";
|
|
22
|
+
import { parseMarkdown, parseMarkdownAll } from "../markdown/parse/index.js";
|
|
23
|
+
import { formatMarkdown } from "../markdown/format/index.js";
|
|
22
24
|
import { ExcelDownloadError, ExcelNotSupportedError } from "./errors.js";
|
|
23
25
|
import { pipeline } from "../stream/index.browser.js";
|
|
24
26
|
import { readableStreamToAsyncIterable } from "../stream/utils.base.js";
|
|
@@ -113,6 +115,57 @@ function createDefaultWriteMapper(dateFormat, dateUTC) {
|
|
|
113
115
|
};
|
|
114
116
|
}
|
|
115
117
|
// =============================================================================
|
|
118
|
+
// Markdown Value Mapper (Internal)
|
|
119
|
+
// =============================================================================
|
|
120
|
+
/**
|
|
121
|
+
* Create a stringify function for Markdown output.
|
|
122
|
+
* Handles hyperlinks, formulas, rich text, dates, errors, and objects.
|
|
123
|
+
*/
|
|
124
|
+
function createMarkdownStringify(dateFormat, dateUTC) {
|
|
125
|
+
const formatter = dateFormat
|
|
126
|
+
? DateFormatter.create(dateFormat, { utc: dateUTC })
|
|
127
|
+
: DateFormatter.iso(dateUTC);
|
|
128
|
+
return function stringify(value) {
|
|
129
|
+
if (value === null || value === undefined) {
|
|
130
|
+
return "";
|
|
131
|
+
}
|
|
132
|
+
if (typeof value === "string") {
|
|
133
|
+
return value;
|
|
134
|
+
}
|
|
135
|
+
if (typeof value === "number" || typeof value === "bigint") {
|
|
136
|
+
return String(value);
|
|
137
|
+
}
|
|
138
|
+
if (typeof value === "boolean") {
|
|
139
|
+
return value ? "true" : "false";
|
|
140
|
+
}
|
|
141
|
+
if (value instanceof Date) {
|
|
142
|
+
return formatter.format(value);
|
|
143
|
+
}
|
|
144
|
+
if (typeof value === "object") {
|
|
145
|
+
const v = value;
|
|
146
|
+
if (v.text || v.hyperlink) {
|
|
147
|
+
return v.hyperlink || v.text || "";
|
|
148
|
+
}
|
|
149
|
+
if (v.formula || v.result) {
|
|
150
|
+
return v.result != null ? String(v.result) : "";
|
|
151
|
+
}
|
|
152
|
+
if (v.richText && Array.isArray(v.richText)) {
|
|
153
|
+
return v.richText.map((r) => r.text).join("");
|
|
154
|
+
}
|
|
155
|
+
if (v.error) {
|
|
156
|
+
return v.error;
|
|
157
|
+
}
|
|
158
|
+
try {
|
|
159
|
+
return JSON.stringify(value);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return "[object Object]";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return String(value);
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// =============================================================================
|
|
116
169
|
// CSV Input Type Detection (Internal)
|
|
117
170
|
// =============================================================================
|
|
118
171
|
function isUrl(input) {
|
|
@@ -560,6 +613,179 @@ class Workbook {
|
|
|
560
613
|
formatter.end();
|
|
561
614
|
await pipelinePromise;
|
|
562
615
|
}
|
|
616
|
+
/**
|
|
617
|
+
* Populate a worksheet from a parsed Markdown table result.
|
|
618
|
+
* Shared by readMarkdown and readMarkdownAll.
|
|
619
|
+
*/
|
|
620
|
+
_populateMarkdownWorksheet(worksheet, result, map) {
|
|
621
|
+
worksheet.addRow(result.headers);
|
|
622
|
+
worksheet._markdownAlignments = result.alignments;
|
|
623
|
+
for (const row of result.rows) {
|
|
624
|
+
if (map) {
|
|
625
|
+
worksheet.addRow(row.map((v, i) => map(v, i)));
|
|
626
|
+
}
|
|
627
|
+
else {
|
|
628
|
+
worksheet.addRow(row);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Read a Markdown table and add as worksheet.
|
|
634
|
+
*
|
|
635
|
+
* @example
|
|
636
|
+
* ```ts
|
|
637
|
+
* // From a Markdown string
|
|
638
|
+
* workbook.readMarkdown("| Name | Age |\n| --- | --- |\n| Alice | 30 |");
|
|
639
|
+
*
|
|
640
|
+
* // With options
|
|
641
|
+
* workbook.readMarkdown(markdownString, { sheetName: "Data", map: (v, col) => Number(v) || v });
|
|
642
|
+
* ```
|
|
643
|
+
*/
|
|
644
|
+
readMarkdown(input, options) {
|
|
645
|
+
const parseResult = parseMarkdown(input, {
|
|
646
|
+
trim: options?.trim,
|
|
647
|
+
unescape: options?.unescape,
|
|
648
|
+
skipEmptyRows: options?.skipEmptyRows,
|
|
649
|
+
maxRows: options?.maxRows,
|
|
650
|
+
convertBr: options?.convertBr
|
|
651
|
+
});
|
|
652
|
+
const worksheet = this.addWorksheet(options?.sheetName);
|
|
653
|
+
this._populateMarkdownWorksheet(worksheet, parseResult, options?.map);
|
|
654
|
+
return worksheet;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Read all Markdown tables from a document, each becoming a separate worksheet.
|
|
658
|
+
*
|
|
659
|
+
* @param input - Markdown string containing one or more tables
|
|
660
|
+
* @param options - Parse options (sheetName is used as prefix: "sheetName", "sheetName_2", ...)
|
|
661
|
+
* @returns Array of created worksheets (empty if no tables found)
|
|
662
|
+
*
|
|
663
|
+
* @example
|
|
664
|
+
* ```ts
|
|
665
|
+
* // Parse a document with multiple tables
|
|
666
|
+
* const sheets = workbook.readMarkdownAll(markdownDoc);
|
|
667
|
+
* console.log(`Created ${sheets.length} worksheets`);
|
|
668
|
+
*
|
|
669
|
+
* // With a naming prefix
|
|
670
|
+
* const sheets = workbook.readMarkdownAll(markdownDoc, { sheetName: "Table" });
|
|
671
|
+
* // Creates "Table", "Table_2", "Table_3", ...
|
|
672
|
+
* ```
|
|
673
|
+
*/
|
|
674
|
+
readMarkdownAll(input, options) {
|
|
675
|
+
const parseResults = parseMarkdownAll(input, {
|
|
676
|
+
trim: options?.trim,
|
|
677
|
+
unescape: options?.unescape,
|
|
678
|
+
skipEmptyRows: options?.skipEmptyRows,
|
|
679
|
+
maxRows: options?.maxRows,
|
|
680
|
+
convertBr: options?.convertBr
|
|
681
|
+
});
|
|
682
|
+
const baseName = options?.sheetName;
|
|
683
|
+
const map = options?.map;
|
|
684
|
+
const worksheets = [];
|
|
685
|
+
for (let t = 0; t < parseResults.length; t++) {
|
|
686
|
+
const name = baseName ? (t === 0 ? baseName : `${baseName}_${t + 1}`) : undefined;
|
|
687
|
+
const worksheet = this.addWorksheet(name);
|
|
688
|
+
this._populateMarkdownWorksheet(worksheet, parseResults[t], map);
|
|
689
|
+
worksheets.push(worksheet);
|
|
690
|
+
}
|
|
691
|
+
return worksheets;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Write worksheet as a Markdown table string.
|
|
695
|
+
*
|
|
696
|
+
* @example
|
|
697
|
+
* ```ts
|
|
698
|
+
* // Write first worksheet
|
|
699
|
+
* const markdownText = workbook.writeMarkdown();
|
|
700
|
+
*
|
|
701
|
+
* // Write specific worksheet with options
|
|
702
|
+
* const markdownText = workbook.writeMarkdown({ sheetName: "Data", padding: true });
|
|
703
|
+
* ```
|
|
704
|
+
*/
|
|
705
|
+
writeMarkdown(options) {
|
|
706
|
+
const worksheet = this.getWorksheet(options?.sheetName || options?.sheetId);
|
|
707
|
+
if (!worksheet) {
|
|
708
|
+
return "";
|
|
709
|
+
}
|
|
710
|
+
const dateFormat = options?.dateFormat;
|
|
711
|
+
const dateUTC = options?.dateUTC;
|
|
712
|
+
const includeEmptyRows = options?.includeEmptyRows !== false;
|
|
713
|
+
// Build stringify function
|
|
714
|
+
const stringify = options?.stringify ?? createMarkdownStringify(dateFormat, dateUTC);
|
|
715
|
+
// Collect all rows from worksheet
|
|
716
|
+
const allRows = [];
|
|
717
|
+
let lastRow = 1;
|
|
718
|
+
worksheet.eachRow((row, rowNumber) => {
|
|
719
|
+
if (includeEmptyRows) {
|
|
720
|
+
while (lastRow++ < rowNumber - 1) {
|
|
721
|
+
allRows.push([]);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
// row.values is a 1-indexed sparse array — use Array.from to fill holes
|
|
725
|
+
// with undefined, then slice(1) to remove the leading 1-indexed slot
|
|
726
|
+
const values = Array.from(row.values).slice(1);
|
|
727
|
+
allRows.push(values);
|
|
728
|
+
lastRow = rowNumber;
|
|
729
|
+
});
|
|
730
|
+
if (allRows.length === 0) {
|
|
731
|
+
return "";
|
|
732
|
+
}
|
|
733
|
+
// First row is the header
|
|
734
|
+
const headerRow = allRows[0];
|
|
735
|
+
const headers = headerRow.map(v => stringify(v));
|
|
736
|
+
const dataRows = allRows.slice(1);
|
|
737
|
+
// Check for stored alignments from a previous readMarkdown
|
|
738
|
+
const storedAlignments = worksheet
|
|
739
|
+
._markdownAlignments;
|
|
740
|
+
// Build column configs
|
|
741
|
+
const columns = options?.columns;
|
|
742
|
+
let resolvedColumns;
|
|
743
|
+
if (!columns && storedAlignments) {
|
|
744
|
+
// Use stored alignments from parsed Markdown
|
|
745
|
+
resolvedColumns = headers.map((h, i) => ({
|
|
746
|
+
header: h,
|
|
747
|
+
alignment: i < storedAlignments.length ? storedAlignments[i] : undefined
|
|
748
|
+
}));
|
|
749
|
+
}
|
|
750
|
+
return formatMarkdown(headers, dataRows, {
|
|
751
|
+
columns: resolvedColumns ?? columns,
|
|
752
|
+
alignment: options?.alignment,
|
|
753
|
+
padding: options?.padding,
|
|
754
|
+
trailingNewline: options?.trailingNewline,
|
|
755
|
+
escapeContent: options?.escapeContent,
|
|
756
|
+
stringify
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
/**
|
|
760
|
+
* Write worksheet to Markdown buffer (Uint8Array).
|
|
761
|
+
*
|
|
762
|
+
* @example
|
|
763
|
+
* ```ts
|
|
764
|
+
* const buffer = workbook.writeMarkdownBuffer();
|
|
765
|
+
* ```
|
|
766
|
+
*/
|
|
767
|
+
writeMarkdownBuffer(options) {
|
|
768
|
+
const markdownString = this.writeMarkdown(options);
|
|
769
|
+
return new TextEncoder().encode(markdownString);
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* Read Markdown from file (Node.js only - throws in browser)
|
|
773
|
+
*/
|
|
774
|
+
async readMarkdownFile(_filename, _options) {
|
|
775
|
+
throw new ExcelNotSupportedError("readMarkdownFile()", "not available in browser. Use readMarkdown(string) instead.");
|
|
776
|
+
}
|
|
777
|
+
/**
|
|
778
|
+
* Read all Markdown tables from file (Node.js only - throws in browser)
|
|
779
|
+
*/
|
|
780
|
+
async readMarkdownAllFile(_filename, _options) {
|
|
781
|
+
throw new ExcelNotSupportedError("readMarkdownAllFile()", "not available in browser. Use readMarkdownAll(string) instead.");
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Write Markdown to file (Node.js only - throws in browser)
|
|
785
|
+
*/
|
|
786
|
+
async writeMarkdownFile(_filename, _options) {
|
|
787
|
+
throw new ExcelNotSupportedError("writeMarkdownFile()", "not available in browser. Use writeMarkdown() and trigger a download instead.");
|
|
788
|
+
}
|
|
563
789
|
// ===========================================================================
|
|
564
790
|
// Static Factory Methods for Streaming
|
|
565
791
|
// ===========================================================================
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Workbook - Node.js Version
|
|
3
3
|
*
|
|
4
|
-
* Extends browser Workbook with Node.js file system support for CSV operations.
|
|
4
|
+
* Extends browser Workbook with Node.js file system support for CSV and Markdown operations.
|
|
5
5
|
*/
|
|
6
6
|
import { Workbook as WorkbookBrowser, type CsvOptions } from "./workbook.browser.js";
|
|
7
7
|
import type { Worksheet } from "./worksheet.js";
|
|
8
|
+
import type { MarkdownOptions } from "../markdown/types.js";
|
|
8
9
|
declare class Workbook extends WorkbookBrowser {
|
|
9
10
|
/**
|
|
10
11
|
* Read CSV from file (Node.js only)
|
|
@@ -27,6 +28,36 @@ declare class Workbook extends WorkbookBrowser {
|
|
|
27
28
|
* ```
|
|
28
29
|
*/
|
|
29
30
|
writeCsvFile(filename: string, options?: CsvOptions): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Read Markdown table from file (Node.js only)
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* await workbook.readMarkdownFile("table.md");
|
|
37
|
+
* await workbook.readMarkdownFile("table.md", { sheetName: "Data" });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
readMarkdownFile(filename: string, options?: MarkdownOptions): Promise<Worksheet>;
|
|
41
|
+
/**
|
|
42
|
+
* Read all Markdown tables from file, each as a separate worksheet (Node.js only)
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* await workbook.readMarkdownAllFile("doc.md");
|
|
47
|
+
* await workbook.readMarkdownAllFile("doc.md", { sheetName: "Table" });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
readMarkdownAllFile(filename: string, options?: MarkdownOptions): Promise<Worksheet[]>;
|
|
51
|
+
/**
|
|
52
|
+
* Write Markdown table to file (Node.js only)
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* await workbook.writeMarkdownFile("output.md");
|
|
57
|
+
* await workbook.writeMarkdownFile("output.md", { sheetName: "Data", padding: true });
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
writeMarkdownFile(filename: string, options?: MarkdownOptions): Promise<void>;
|
|
30
61
|
}
|
|
31
62
|
export { Workbook };
|
|
32
63
|
export type { CsvOptions, CsvInput, WorkbookModel, WorkbookMedia } from "./workbook.browser.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Workbook - Node.js Version
|
|
3
3
|
*
|
|
4
|
-
* Extends browser Workbook with Node.js file system support for CSV operations.
|
|
4
|
+
* Extends browser Workbook with Node.js file system support for CSV and Markdown operations.
|
|
5
5
|
*/
|
|
6
|
-
import { fileExists, createReadStream, createWriteStream } from "../../utils/fs.browser.js";
|
|
6
|
+
import { fileExists, createReadStream, createWriteStream, readFileText, writeFileText } from "../../utils/fs.browser.js";
|
|
7
7
|
import { Workbook as WorkbookBrowser } from "./workbook.browser.js";
|
|
8
8
|
import { ExcelFileError } from "./errors.js";
|
|
9
9
|
// =============================================================================
|
|
@@ -57,5 +57,50 @@ class Workbook extends WorkbookBrowser {
|
|
|
57
57
|
}
|
|
58
58
|
return this._writeCsvStream(writeStream, options);
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Read Markdown table from file (Node.js only)
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* await workbook.readMarkdownFile("table.md");
|
|
66
|
+
* await workbook.readMarkdownFile("table.md", { sheetName: "Data" });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
async readMarkdownFile(filename, options) {
|
|
70
|
+
if (!(await fileExists(filename))) {
|
|
71
|
+
throw new ExcelFileError(filename, "read", "file not found");
|
|
72
|
+
}
|
|
73
|
+
const content = await readFileText(filename);
|
|
74
|
+
return this.readMarkdown(content, options);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Read all Markdown tables from file, each as a separate worksheet (Node.js only)
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* await workbook.readMarkdownAllFile("doc.md");
|
|
82
|
+
* await workbook.readMarkdownAllFile("doc.md", { sheetName: "Table" });
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
async readMarkdownAllFile(filename, options) {
|
|
86
|
+
if (!(await fileExists(filename))) {
|
|
87
|
+
throw new ExcelFileError(filename, "read", "file not found");
|
|
88
|
+
}
|
|
89
|
+
const content = await readFileText(filename);
|
|
90
|
+
return this.readMarkdownAll(content, options);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Write Markdown table to file (Node.js only)
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* await workbook.writeMarkdownFile("output.md");
|
|
98
|
+
* await workbook.writeMarkdownFile("output.md", { sheetName: "Data", padding: true });
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
async writeMarkdownFile(filename, options) {
|
|
102
|
+
const markdownString = this.writeMarkdown(options);
|
|
103
|
+
await writeFileText(filename, markdownString);
|
|
104
|
+
}
|
|
60
105
|
}
|
|
61
106
|
export { Workbook };
|
|
@@ -46,6 +46,13 @@ class StreamingZipWriterAdapter {
|
|
|
46
46
|
// Backpressure tracking
|
|
47
47
|
this._needsDrain = false;
|
|
48
48
|
this._drainResolvers = [];
|
|
49
|
+
// Count of in-flight async write() calls whose backpressure result is unknown.
|
|
50
|
+
// waitForDrain() must wait for this to reach 0 before checking _needsDrain.
|
|
51
|
+
this._pendingWrites = 0;
|
|
52
|
+
this._pendingWriteResolvers = [];
|
|
53
|
+
// Buffer errors that occur before _finalize registers its error listener,
|
|
54
|
+
// so async compression errors during writeToZip() are never silently lost.
|
|
55
|
+
this._earlyError = null;
|
|
49
56
|
this.level = options?.level ?? 6;
|
|
50
57
|
this.modTime = options?.modTime;
|
|
51
58
|
this.timestamps = options?.timestamps;
|
|
@@ -69,6 +76,15 @@ class StreamingZipWriterAdapter {
|
|
|
69
76
|
});
|
|
70
77
|
}
|
|
71
78
|
_emit(event, ...args) {
|
|
79
|
+
// Buffer error events that fire before any listener is registered,
|
|
80
|
+
// so _finalize() can surface them even if it registers late.
|
|
81
|
+
if (event === "error") {
|
|
82
|
+
const callbacks = this.events.get(event);
|
|
83
|
+
if (!callbacks || callbacks.size === 0) {
|
|
84
|
+
this._earlyError = args[0] instanceof Error ? args[0] : new Error(String(args[0]));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
72
88
|
const callbacks = this.events.get(event);
|
|
73
89
|
if (!callbacks) {
|
|
74
90
|
return;
|
|
@@ -83,12 +99,21 @@ class StreamingZipWriterAdapter {
|
|
|
83
99
|
*/
|
|
84
100
|
_checkBackpressure(ok) {
|
|
85
101
|
if (ok instanceof Promise) {
|
|
102
|
+
this._pendingWrites++;
|
|
86
103
|
ok.then(result => {
|
|
87
104
|
if (result === false) {
|
|
88
105
|
this._needsDrain = true;
|
|
89
106
|
}
|
|
90
107
|
}, () => { } // write errors surface via the stream's error event
|
|
91
|
-
)
|
|
108
|
+
).finally(() => {
|
|
109
|
+
this._pendingWrites--;
|
|
110
|
+
if (this._pendingWrites === 0) {
|
|
111
|
+
const resolvers = this._pendingWriteResolvers.splice(0);
|
|
112
|
+
for (const resolve of resolvers) {
|
|
113
|
+
resolve();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
});
|
|
92
117
|
return;
|
|
93
118
|
}
|
|
94
119
|
if (ok === false) {
|
|
@@ -99,6 +124,12 @@ class StreamingZipWriterAdapter {
|
|
|
99
124
|
const callbacks = this.events.get(event) || new Set();
|
|
100
125
|
callbacks.add(callback);
|
|
101
126
|
this.events.set(event, callbacks);
|
|
127
|
+
// If an error was buffered before any listener was registered, deliver it now.
|
|
128
|
+
if (event === "error" && this._earlyError) {
|
|
129
|
+
const err = this._earlyError;
|
|
130
|
+
this._earlyError = null;
|
|
131
|
+
callback(err);
|
|
132
|
+
}
|
|
102
133
|
return this;
|
|
103
134
|
}
|
|
104
135
|
once(event, callback) {
|
|
@@ -134,11 +165,18 @@ class StreamingZipWriterAdapter {
|
|
|
134
165
|
}
|
|
135
166
|
/**
|
|
136
167
|
* Wait for the downstream writable to drain if it signaled backpressure.
|
|
137
|
-
*
|
|
168
|
+
* If any write() calls are still in-flight (returned a Promise that hasn't
|
|
169
|
+
* settled), waits for all of them first so the backpressure signal isn't missed.
|
|
138
170
|
*/
|
|
139
|
-
waitForDrain() {
|
|
171
|
+
async waitForDrain() {
|
|
172
|
+
// Wait for all in-flight async writes to settle so _needsDrain is up to date.
|
|
173
|
+
if (this._pendingWrites > 0) {
|
|
174
|
+
await new Promise(resolve => {
|
|
175
|
+
this._pendingWriteResolvers.push(resolve);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
140
178
|
if (!this._needsDrain || !this.pipedStream) {
|
|
141
|
-
return
|
|
179
|
+
return;
|
|
142
180
|
}
|
|
143
181
|
return new Promise(resolve => {
|
|
144
182
|
this._drainResolvers.push(resolve);
|