@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
|
@@ -19,6 +19,7 @@ exports.createGzipStream = createGzipStream;
|
|
|
19
19
|
exports.createGunzipStream = createGunzipStream;
|
|
20
20
|
exports.createZlibStream = createZlibStream;
|
|
21
21
|
exports.createUnzlibStream = createUnzlibStream;
|
|
22
|
+
exports.hasNativeAsyncDeflate = hasNativeAsyncDeflate;
|
|
22
23
|
const event_emitter_1 = require("../../../utils/event-emitter.js");
|
|
23
24
|
const deflate_fallback_1 = require("./deflate-fallback.js");
|
|
24
25
|
Object.defineProperty(exports, "SyncDeflater", { enumerable: true, get: function () { return deflate_fallback_1.SyncDeflater; } });
|
|
@@ -264,10 +265,13 @@ function createStreamCodec(type, options) {
|
|
|
264
265
|
if (options.useWorker && (0, index_browser_1.hasWorkerSupport)()) {
|
|
265
266
|
return createWorkerStreamCodec(type, options.workerPool, level, options.allowTransfer);
|
|
266
267
|
}
|
|
267
|
-
|
|
268
|
+
// Use native CompressionStream/DecompressionStream when the required
|
|
269
|
+
// direction is available. Compression only needs CompressionStream;
|
|
270
|
+
// decompression only needs DecompressionStream.
|
|
271
|
+
if (type === "deflate" ? (0, compress_base_1.hasDeflateRawCompressionStream)() : (0, compress_base_1.hasDeflateRawWebStreams)()) {
|
|
268
272
|
return createNativeWebStreamCodec("deflate-raw", type === "deflate");
|
|
269
273
|
}
|
|
270
|
-
return new BufferedCodec(type === "deflate" ? deflate_fallback_1.deflateRawCompressed : deflate_fallback_1.inflateRaw);
|
|
274
|
+
return new BufferedCodec(type === "deflate" ? data => (0, deflate_fallback_1.deflateRawCompressed)(data, level) : deflate_fallback_1.inflateRaw);
|
|
271
275
|
}
|
|
272
276
|
// =============================================================================
|
|
273
277
|
// Public API
|
|
@@ -319,3 +323,12 @@ function createZlibStream(options = {}) {
|
|
|
319
323
|
function createUnzlibStream(options = {}) {
|
|
320
324
|
return createWrappedStream(ZLIB_CONFIG, false, options);
|
|
321
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Returns true when the browser supports native `CompressionStream("deflate-raw")`,
|
|
328
|
+
* signalling that `push()` should prefer the async path over `SyncDeflater`.
|
|
329
|
+
*
|
|
330
|
+
* Only checks for compression support — decompression is not needed for writing.
|
|
331
|
+
*/
|
|
332
|
+
function hasNativeAsyncDeflate() {
|
|
333
|
+
return (0, compress_base_1.hasDeflateRawCompressionStream)();
|
|
334
|
+
}
|
|
@@ -14,6 +14,7 @@ exports.createGzipStream = createGzipStream;
|
|
|
14
14
|
exports.createGunzipStream = createGunzipStream;
|
|
15
15
|
exports.createZlibStream = createZlibStream;
|
|
16
16
|
exports.createUnzlibStream = createUnzlibStream;
|
|
17
|
+
exports.hasNativeAsyncDeflate = hasNativeAsyncDeflate;
|
|
17
18
|
const zlib_1 = require("zlib");
|
|
18
19
|
const _stream_1 = require("../../stream/index.js");
|
|
19
20
|
const defaults_1 = require("../shared/defaults.js");
|
|
@@ -177,3 +178,10 @@ class SyncDeflater {
|
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
180
|
exports.SyncDeflater = SyncDeflater;
|
|
181
|
+
/**
|
|
182
|
+
* On Node.js, `zlib.deflateRawSync` is native and fast — no need to detour
|
|
183
|
+
* through the async streaming path. Always returns false.
|
|
184
|
+
*/
|
|
185
|
+
function hasNativeAsyncDeflate() {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
@@ -574,6 +574,12 @@ class ZipDeflateFile {
|
|
|
574
574
|
this._tapCallback(promise, callback);
|
|
575
575
|
return promise;
|
|
576
576
|
}
|
|
577
|
+
// If a previous async operation already failed, don't do more work.
|
|
578
|
+
if (this._completeError) {
|
|
579
|
+
const promise = Promise.reject(this._completeError);
|
|
580
|
+
this._tapCallback(promise, callback);
|
|
581
|
+
return promise;
|
|
582
|
+
}
|
|
577
583
|
if (this._deflateWanted === null) {
|
|
578
584
|
this._accumulateSampleLen(data);
|
|
579
585
|
if (!this._shouldDecide(final)) {
|
|
@@ -616,7 +622,16 @@ class ZipDeflateFile {
|
|
|
616
622
|
* memory growth when callers push data in a tight synchronous loop.
|
|
617
623
|
*/
|
|
618
624
|
push(data, final = false, callback) {
|
|
619
|
-
|
|
625
|
+
// Use the synchronous path only when:
|
|
626
|
+
// 1. No async deflate stream is already active
|
|
627
|
+
// 2. No encryption (which requires async crypto)
|
|
628
|
+
// 3. No native async deflate available (browser CompressionStream)
|
|
629
|
+
//
|
|
630
|
+
// When a native async CompressionStream("deflate-raw") is available
|
|
631
|
+
// (modern browsers), prefer the async path — it produces better
|
|
632
|
+
// compression (Dynamic Huffman via the browser's native engine) and
|
|
633
|
+
// doesn't block the main thread.
|
|
634
|
+
if (!this._deflate && this._encryptionMethod === "none" && !(0, streaming_compress_1.hasNativeAsyncDeflate)()) {
|
|
620
635
|
try {
|
|
621
636
|
this._pushSyncPath(data, final);
|
|
622
637
|
callback?.();
|
|
@@ -627,7 +642,10 @@ class ZipDeflateFile {
|
|
|
627
642
|
}
|
|
628
643
|
return Promise.resolve();
|
|
629
644
|
}
|
|
630
|
-
|
|
645
|
+
// Chain the async push so calls are serialized. Use a recovery wrapper
|
|
646
|
+
// so that a single failed push does not break the chain for subsequent
|
|
647
|
+
// pushes — errors are surfaced via onerror/rejectComplete instead.
|
|
648
|
+
const promise = (this._pushChain = this._pushChain.then(() => this._pushUnchained(data, final, callback), () => this._pushUnchained(data, final, callback)));
|
|
631
649
|
// Prevent unhandled rejection when callers intentionally ignore the Promise.
|
|
632
650
|
promise.catch(() => { });
|
|
633
651
|
return promise;
|
|
@@ -698,6 +716,12 @@ class ZipDeflateFile {
|
|
|
698
716
|
this._syncDeflater = null;
|
|
699
717
|
}
|
|
700
718
|
this._emitDataDescriptor();
|
|
719
|
+
// _emitDataDescriptor may call _rejectComplete instead of throwing
|
|
720
|
+
// (e.g. ZIP64 required but zip64=false). In the sync path, surface
|
|
721
|
+
// this as a throw so push() can reject properly.
|
|
722
|
+
if (this._completeError) {
|
|
723
|
+
throw this._completeError;
|
|
724
|
+
}
|
|
701
725
|
}
|
|
702
726
|
/**
|
|
703
727
|
* Emit local file header with Data Descriptor flag
|
|
@@ -22,6 +22,8 @@ const parse_1 = require("../csv/parse/index.js");
|
|
|
22
22
|
const format_1 = require("../csv/format/index.js");
|
|
23
23
|
const stream_1 = require("../csv/stream/index.js");
|
|
24
24
|
const number_1 = require("../csv/utils/number.js");
|
|
25
|
+
const index_1 = require("../markdown/parse/index.js");
|
|
26
|
+
const index_2 = require("../markdown/format/index.js");
|
|
25
27
|
const errors_1 = require("./errors.js");
|
|
26
28
|
const _stream_1 = require("../stream/index.js");
|
|
27
29
|
const utils_base_1 = require("../stream/utils.base.js");
|
|
@@ -116,6 +118,57 @@ function createDefaultWriteMapper(dateFormat, dateUTC) {
|
|
|
116
118
|
};
|
|
117
119
|
}
|
|
118
120
|
// =============================================================================
|
|
121
|
+
// Markdown Value Mapper (Internal)
|
|
122
|
+
// =============================================================================
|
|
123
|
+
/**
|
|
124
|
+
* Create a stringify function for Markdown output.
|
|
125
|
+
* Handles hyperlinks, formulas, rich text, dates, errors, and objects.
|
|
126
|
+
*/
|
|
127
|
+
function createMarkdownStringify(dateFormat, dateUTC) {
|
|
128
|
+
const formatter = dateFormat
|
|
129
|
+
? datetime_1.DateFormatter.create(dateFormat, { utc: dateUTC })
|
|
130
|
+
: datetime_1.DateFormatter.iso(dateUTC);
|
|
131
|
+
return function stringify(value) {
|
|
132
|
+
if (value === null || value === undefined) {
|
|
133
|
+
return "";
|
|
134
|
+
}
|
|
135
|
+
if (typeof value === "string") {
|
|
136
|
+
return value;
|
|
137
|
+
}
|
|
138
|
+
if (typeof value === "number" || typeof value === "bigint") {
|
|
139
|
+
return String(value);
|
|
140
|
+
}
|
|
141
|
+
if (typeof value === "boolean") {
|
|
142
|
+
return value ? "true" : "false";
|
|
143
|
+
}
|
|
144
|
+
if (value instanceof Date) {
|
|
145
|
+
return formatter.format(value);
|
|
146
|
+
}
|
|
147
|
+
if (typeof value === "object") {
|
|
148
|
+
const v = value;
|
|
149
|
+
if (v.text || v.hyperlink) {
|
|
150
|
+
return v.hyperlink || v.text || "";
|
|
151
|
+
}
|
|
152
|
+
if (v.formula || v.result) {
|
|
153
|
+
return v.result != null ? String(v.result) : "";
|
|
154
|
+
}
|
|
155
|
+
if (v.richText && Array.isArray(v.richText)) {
|
|
156
|
+
return v.richText.map((r) => r.text).join("");
|
|
157
|
+
}
|
|
158
|
+
if (v.error) {
|
|
159
|
+
return v.error;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
return JSON.stringify(value);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return "[object Object]";
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return String(value);
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
// =============================================================================
|
|
119
172
|
// CSV Input Type Detection (Internal)
|
|
120
173
|
// =============================================================================
|
|
121
174
|
function isUrl(input) {
|
|
@@ -563,6 +616,179 @@ class Workbook {
|
|
|
563
616
|
formatter.end();
|
|
564
617
|
await pipelinePromise;
|
|
565
618
|
}
|
|
619
|
+
/**
|
|
620
|
+
* Populate a worksheet from a parsed Markdown table result.
|
|
621
|
+
* Shared by readMarkdown and readMarkdownAll.
|
|
622
|
+
*/
|
|
623
|
+
_populateMarkdownWorksheet(worksheet, result, map) {
|
|
624
|
+
worksheet.addRow(result.headers);
|
|
625
|
+
worksheet._markdownAlignments = result.alignments;
|
|
626
|
+
for (const row of result.rows) {
|
|
627
|
+
if (map) {
|
|
628
|
+
worksheet.addRow(row.map((v, i) => map(v, i)));
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
worksheet.addRow(row);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Read a Markdown table and add as worksheet.
|
|
637
|
+
*
|
|
638
|
+
* @example
|
|
639
|
+
* ```ts
|
|
640
|
+
* // From a Markdown string
|
|
641
|
+
* workbook.readMarkdown("| Name | Age |\n| --- | --- |\n| Alice | 30 |");
|
|
642
|
+
*
|
|
643
|
+
* // With options
|
|
644
|
+
* workbook.readMarkdown(markdownString, { sheetName: "Data", map: (v, col) => Number(v) || v });
|
|
645
|
+
* ```
|
|
646
|
+
*/
|
|
647
|
+
readMarkdown(input, options) {
|
|
648
|
+
const parseResult = (0, index_1.parseMarkdown)(input, {
|
|
649
|
+
trim: options?.trim,
|
|
650
|
+
unescape: options?.unescape,
|
|
651
|
+
skipEmptyRows: options?.skipEmptyRows,
|
|
652
|
+
maxRows: options?.maxRows,
|
|
653
|
+
convertBr: options?.convertBr
|
|
654
|
+
});
|
|
655
|
+
const worksheet = this.addWorksheet(options?.sheetName);
|
|
656
|
+
this._populateMarkdownWorksheet(worksheet, parseResult, options?.map);
|
|
657
|
+
return worksheet;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Read all Markdown tables from a document, each becoming a separate worksheet.
|
|
661
|
+
*
|
|
662
|
+
* @param input - Markdown string containing one or more tables
|
|
663
|
+
* @param options - Parse options (sheetName is used as prefix: "sheetName", "sheetName_2", ...)
|
|
664
|
+
* @returns Array of created worksheets (empty if no tables found)
|
|
665
|
+
*
|
|
666
|
+
* @example
|
|
667
|
+
* ```ts
|
|
668
|
+
* // Parse a document with multiple tables
|
|
669
|
+
* const sheets = workbook.readMarkdownAll(markdownDoc);
|
|
670
|
+
* console.log(`Created ${sheets.length} worksheets`);
|
|
671
|
+
*
|
|
672
|
+
* // With a naming prefix
|
|
673
|
+
* const sheets = workbook.readMarkdownAll(markdownDoc, { sheetName: "Table" });
|
|
674
|
+
* // Creates "Table", "Table_2", "Table_3", ...
|
|
675
|
+
* ```
|
|
676
|
+
*/
|
|
677
|
+
readMarkdownAll(input, options) {
|
|
678
|
+
const parseResults = (0, index_1.parseMarkdownAll)(input, {
|
|
679
|
+
trim: options?.trim,
|
|
680
|
+
unescape: options?.unescape,
|
|
681
|
+
skipEmptyRows: options?.skipEmptyRows,
|
|
682
|
+
maxRows: options?.maxRows,
|
|
683
|
+
convertBr: options?.convertBr
|
|
684
|
+
});
|
|
685
|
+
const baseName = options?.sheetName;
|
|
686
|
+
const map = options?.map;
|
|
687
|
+
const worksheets = [];
|
|
688
|
+
for (let t = 0; t < parseResults.length; t++) {
|
|
689
|
+
const name = baseName ? (t === 0 ? baseName : `${baseName}_${t + 1}`) : undefined;
|
|
690
|
+
const worksheet = this.addWorksheet(name);
|
|
691
|
+
this._populateMarkdownWorksheet(worksheet, parseResults[t], map);
|
|
692
|
+
worksheets.push(worksheet);
|
|
693
|
+
}
|
|
694
|
+
return worksheets;
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Write worksheet as a Markdown table string.
|
|
698
|
+
*
|
|
699
|
+
* @example
|
|
700
|
+
* ```ts
|
|
701
|
+
* // Write first worksheet
|
|
702
|
+
* const markdownText = workbook.writeMarkdown();
|
|
703
|
+
*
|
|
704
|
+
* // Write specific worksheet with options
|
|
705
|
+
* const markdownText = workbook.writeMarkdown({ sheetName: "Data", padding: true });
|
|
706
|
+
* ```
|
|
707
|
+
*/
|
|
708
|
+
writeMarkdown(options) {
|
|
709
|
+
const worksheet = this.getWorksheet(options?.sheetName || options?.sheetId);
|
|
710
|
+
if (!worksheet) {
|
|
711
|
+
return "";
|
|
712
|
+
}
|
|
713
|
+
const dateFormat = options?.dateFormat;
|
|
714
|
+
const dateUTC = options?.dateUTC;
|
|
715
|
+
const includeEmptyRows = options?.includeEmptyRows !== false;
|
|
716
|
+
// Build stringify function
|
|
717
|
+
const stringify = options?.stringify ?? createMarkdownStringify(dateFormat, dateUTC);
|
|
718
|
+
// Collect all rows from worksheet
|
|
719
|
+
const allRows = [];
|
|
720
|
+
let lastRow = 1;
|
|
721
|
+
worksheet.eachRow((row, rowNumber) => {
|
|
722
|
+
if (includeEmptyRows) {
|
|
723
|
+
while (lastRow++ < rowNumber - 1) {
|
|
724
|
+
allRows.push([]);
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
// row.values is a 1-indexed sparse array — use Array.from to fill holes
|
|
728
|
+
// with undefined, then slice(1) to remove the leading 1-indexed slot
|
|
729
|
+
const values = Array.from(row.values).slice(1);
|
|
730
|
+
allRows.push(values);
|
|
731
|
+
lastRow = rowNumber;
|
|
732
|
+
});
|
|
733
|
+
if (allRows.length === 0) {
|
|
734
|
+
return "";
|
|
735
|
+
}
|
|
736
|
+
// First row is the header
|
|
737
|
+
const headerRow = allRows[0];
|
|
738
|
+
const headers = headerRow.map(v => stringify(v));
|
|
739
|
+
const dataRows = allRows.slice(1);
|
|
740
|
+
// Check for stored alignments from a previous readMarkdown
|
|
741
|
+
const storedAlignments = worksheet
|
|
742
|
+
._markdownAlignments;
|
|
743
|
+
// Build column configs
|
|
744
|
+
const columns = options?.columns;
|
|
745
|
+
let resolvedColumns;
|
|
746
|
+
if (!columns && storedAlignments) {
|
|
747
|
+
// Use stored alignments from parsed Markdown
|
|
748
|
+
resolvedColumns = headers.map((h, i) => ({
|
|
749
|
+
header: h,
|
|
750
|
+
alignment: i < storedAlignments.length ? storedAlignments[i] : undefined
|
|
751
|
+
}));
|
|
752
|
+
}
|
|
753
|
+
return (0, index_2.formatMarkdown)(headers, dataRows, {
|
|
754
|
+
columns: resolvedColumns ?? columns,
|
|
755
|
+
alignment: options?.alignment,
|
|
756
|
+
padding: options?.padding,
|
|
757
|
+
trailingNewline: options?.trailingNewline,
|
|
758
|
+
escapeContent: options?.escapeContent,
|
|
759
|
+
stringify
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
/**
|
|
763
|
+
* Write worksheet to Markdown buffer (Uint8Array).
|
|
764
|
+
*
|
|
765
|
+
* @example
|
|
766
|
+
* ```ts
|
|
767
|
+
* const buffer = workbook.writeMarkdownBuffer();
|
|
768
|
+
* ```
|
|
769
|
+
*/
|
|
770
|
+
writeMarkdownBuffer(options) {
|
|
771
|
+
const markdownString = this.writeMarkdown(options);
|
|
772
|
+
return new TextEncoder().encode(markdownString);
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Read Markdown from file (Node.js only - throws in browser)
|
|
776
|
+
*/
|
|
777
|
+
async readMarkdownFile(_filename, _options) {
|
|
778
|
+
throw new errors_1.ExcelNotSupportedError("readMarkdownFile()", "not available in browser. Use readMarkdown(string) instead.");
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Read all Markdown tables from file (Node.js only - throws in browser)
|
|
782
|
+
*/
|
|
783
|
+
async readMarkdownAllFile(_filename, _options) {
|
|
784
|
+
throw new errors_1.ExcelNotSupportedError("readMarkdownAllFile()", "not available in browser. Use readMarkdownAll(string) instead.");
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Write Markdown to file (Node.js only - throws in browser)
|
|
788
|
+
*/
|
|
789
|
+
async writeMarkdownFile(_filename, _options) {
|
|
790
|
+
throw new errors_1.ExcelNotSupportedError("writeMarkdownFile()", "not available in browser. Use writeMarkdown() and trigger a download instead.");
|
|
791
|
+
}
|
|
566
792
|
// ===========================================================================
|
|
567
793
|
// Static Factory Methods for Streaming
|
|
568
794
|
// ===========================================================================
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Workbook - Node.js Version
|
|
4
4
|
*
|
|
5
|
-
* Extends browser Workbook with Node.js file system support for CSV operations.
|
|
5
|
+
* Extends browser Workbook with Node.js file system support for CSV and Markdown operations.
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.Workbook = void 0;
|
|
@@ -60,5 +60,50 @@ class Workbook extends workbook_browser_1.Workbook {
|
|
|
60
60
|
}
|
|
61
61
|
return this._writeCsvStream(writeStream, options);
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Read Markdown table from file (Node.js only)
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* await workbook.readMarkdownFile("table.md");
|
|
69
|
+
* await workbook.readMarkdownFile("table.md", { sheetName: "Data" });
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
async readMarkdownFile(filename, options) {
|
|
73
|
+
if (!(await (0, fs_1.fileExists)(filename))) {
|
|
74
|
+
throw new errors_1.ExcelFileError(filename, "read", "file not found");
|
|
75
|
+
}
|
|
76
|
+
const content = await (0, fs_1.readFileText)(filename);
|
|
77
|
+
return this.readMarkdown(content, options);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Read all Markdown tables from file, each as a separate worksheet (Node.js only)
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```ts
|
|
84
|
+
* await workbook.readMarkdownAllFile("doc.md");
|
|
85
|
+
* await workbook.readMarkdownAllFile("doc.md", { sheetName: "Table" });
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
async readMarkdownAllFile(filename, options) {
|
|
89
|
+
if (!(await (0, fs_1.fileExists)(filename))) {
|
|
90
|
+
throw new errors_1.ExcelFileError(filename, "read", "file not found");
|
|
91
|
+
}
|
|
92
|
+
const content = await (0, fs_1.readFileText)(filename);
|
|
93
|
+
return this.readMarkdownAll(content, options);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Write Markdown table to file (Node.js only)
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* await workbook.writeMarkdownFile("output.md");
|
|
101
|
+
* await workbook.writeMarkdownFile("output.md", { sheetName: "Data", padding: true });
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
async writeMarkdownFile(filename, options) {
|
|
105
|
+
const markdownString = this.writeMarkdown(options);
|
|
106
|
+
await (0, fs_1.writeFileText)(filename, markdownString);
|
|
107
|
+
}
|
|
63
108
|
}
|
|
64
109
|
exports.Workbook = Workbook;
|
|
@@ -49,6 +49,13 @@ class StreamingZipWriterAdapter {
|
|
|
49
49
|
// Backpressure tracking
|
|
50
50
|
this._needsDrain = false;
|
|
51
51
|
this._drainResolvers = [];
|
|
52
|
+
// Count of in-flight async write() calls whose backpressure result is unknown.
|
|
53
|
+
// waitForDrain() must wait for this to reach 0 before checking _needsDrain.
|
|
54
|
+
this._pendingWrites = 0;
|
|
55
|
+
this._pendingWriteResolvers = [];
|
|
56
|
+
// Buffer errors that occur before _finalize registers its error listener,
|
|
57
|
+
// so async compression errors during writeToZip() are never silently lost.
|
|
58
|
+
this._earlyError = null;
|
|
52
59
|
this.level = options?.level ?? 6;
|
|
53
60
|
this.modTime = options?.modTime;
|
|
54
61
|
this.timestamps = options?.timestamps;
|
|
@@ -72,6 +79,15 @@ class StreamingZipWriterAdapter {
|
|
|
72
79
|
});
|
|
73
80
|
}
|
|
74
81
|
_emit(event, ...args) {
|
|
82
|
+
// Buffer error events that fire before any listener is registered,
|
|
83
|
+
// so _finalize() can surface them even if it registers late.
|
|
84
|
+
if (event === "error") {
|
|
85
|
+
const callbacks = this.events.get(event);
|
|
86
|
+
if (!callbacks || callbacks.size === 0) {
|
|
87
|
+
this._earlyError = args[0] instanceof Error ? args[0] : new Error(String(args[0]));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
75
91
|
const callbacks = this.events.get(event);
|
|
76
92
|
if (!callbacks) {
|
|
77
93
|
return;
|
|
@@ -86,12 +102,21 @@ class StreamingZipWriterAdapter {
|
|
|
86
102
|
*/
|
|
87
103
|
_checkBackpressure(ok) {
|
|
88
104
|
if (ok instanceof Promise) {
|
|
105
|
+
this._pendingWrites++;
|
|
89
106
|
ok.then(result => {
|
|
90
107
|
if (result === false) {
|
|
91
108
|
this._needsDrain = true;
|
|
92
109
|
}
|
|
93
110
|
}, () => { } // write errors surface via the stream's error event
|
|
94
|
-
)
|
|
111
|
+
).finally(() => {
|
|
112
|
+
this._pendingWrites--;
|
|
113
|
+
if (this._pendingWrites === 0) {
|
|
114
|
+
const resolvers = this._pendingWriteResolvers.splice(0);
|
|
115
|
+
for (const resolve of resolvers) {
|
|
116
|
+
resolve();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
95
120
|
return;
|
|
96
121
|
}
|
|
97
122
|
if (ok === false) {
|
|
@@ -102,6 +127,12 @@ class StreamingZipWriterAdapter {
|
|
|
102
127
|
const callbacks = this.events.get(event) || new Set();
|
|
103
128
|
callbacks.add(callback);
|
|
104
129
|
this.events.set(event, callbacks);
|
|
130
|
+
// If an error was buffered before any listener was registered, deliver it now.
|
|
131
|
+
if (event === "error" && this._earlyError) {
|
|
132
|
+
const err = this._earlyError;
|
|
133
|
+
this._earlyError = null;
|
|
134
|
+
callback(err);
|
|
135
|
+
}
|
|
105
136
|
return this;
|
|
106
137
|
}
|
|
107
138
|
once(event, callback) {
|
|
@@ -137,11 +168,18 @@ class StreamingZipWriterAdapter {
|
|
|
137
168
|
}
|
|
138
169
|
/**
|
|
139
170
|
* Wait for the downstream writable to drain if it signaled backpressure.
|
|
140
|
-
*
|
|
171
|
+
* If any write() calls are still in-flight (returned a Promise that hasn't
|
|
172
|
+
* settled), waits for all of them first so the backpressure signal isn't missed.
|
|
141
173
|
*/
|
|
142
|
-
waitForDrain() {
|
|
174
|
+
async waitForDrain() {
|
|
175
|
+
// Wait for all in-flight async writes to settle so _needsDrain is up to date.
|
|
176
|
+
if (this._pendingWrites > 0) {
|
|
177
|
+
await new Promise(resolve => {
|
|
178
|
+
this._pendingWriteResolvers.push(resolve);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
143
181
|
if (!this._needsDrain || !this.pipedStream) {
|
|
144
|
-
return
|
|
182
|
+
return;
|
|
145
183
|
}
|
|
146
184
|
return new Promise(resolve => {
|
|
147
185
|
this._drainResolvers.push(resolve);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Markdown Module Constants
|
|
4
|
+
*
|
|
5
|
+
* Shared constants used across the Markdown module.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.NEWLINE_IN_CELL = exports.BR_TAG_REGEX = exports.UNESCAPE_REGEX = exports.ESCAPE_AND_NEWLINE = exports.LINEBREAK_REGEX = void 0;
|
|
9
|
+
/**
|
|
10
|
+
* Pre-compiled regex for line splitting (matches CR, LF, or CRLF)
|
|
11
|
+
*/
|
|
12
|
+
exports.LINEBREAK_REGEX = /\r\n|\r|\n/;
|
|
13
|
+
/**
|
|
14
|
+
* Characters that need escaping in Markdown table cells.
|
|
15
|
+
* Also matches CRLF/CR/LF so that escaping + newline conversion
|
|
16
|
+
* can be done in a single `replace()` call.
|
|
17
|
+
* Note: `\r\n` must come before `\r` to match CRLF as a single unit.
|
|
18
|
+
*/
|
|
19
|
+
exports.ESCAPE_AND_NEWLINE = /\r\n|[|\\\r\n]/g;
|
|
20
|
+
/**
|
|
21
|
+
* Regex to unescape Markdown table cell content (`\|` → `|`, `\\` → `\`)
|
|
22
|
+
*/
|
|
23
|
+
exports.UNESCAPE_REGEX = /\\([|\\])/g;
|
|
24
|
+
/**
|
|
25
|
+
* Regex to match `<br>`, `<br/>`, or `<br />` tags (case-insensitive).
|
|
26
|
+
* Used to convert multiline cell representations back to newlines during parsing.
|
|
27
|
+
*/
|
|
28
|
+
exports.BR_TAG_REGEX = /<br\s*\/?>/gi;
|
|
29
|
+
/**
|
|
30
|
+
* Regex to match literal newlines (CR, LF, or CRLF) in cell content.
|
|
31
|
+
* Used when escaping is disabled but newline conversion is still needed.
|
|
32
|
+
*/
|
|
33
|
+
exports.NEWLINE_IN_CELL = /\r\n|\r|\n/g;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Markdown module error types.
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MarkdownParseError = exports.MarkdownError = void 0;
|
|
7
|
+
const errors_1 = require("../../utils/errors.js");
|
|
8
|
+
/**
|
|
9
|
+
* Base class for all Markdown-related errors.
|
|
10
|
+
*/
|
|
11
|
+
class MarkdownError extends errors_1.BaseError {
|
|
12
|
+
constructor() {
|
|
13
|
+
super(...arguments);
|
|
14
|
+
this.name = "MarkdownError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.MarkdownError = MarkdownError;
|
|
18
|
+
/**
|
|
19
|
+
* Error thrown when Markdown parsing fails.
|
|
20
|
+
*/
|
|
21
|
+
class MarkdownParseError extends MarkdownError {
|
|
22
|
+
constructor(message, line, options) {
|
|
23
|
+
super(`Line ${line}: ${message}`, options);
|
|
24
|
+
this.name = "MarkdownParseError";
|
|
25
|
+
this.line = line;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.MarkdownParseError = MarkdownParseError;
|