@ricsam/excel-to-formula-engine 0.1.0 → 0.2.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 +50 -1
- package/dist/index.cjs +309 -5
- package/dist/index.cjs.map +8 -6
- package/dist/index.mjs +309 -5
- package/dist/index.mjs.map +8 -6
- package/dist/types/index.d.ts +27 -2
- package/dist/types/src/csv/convert.d.ts +31 -0
- package/dist/types/src/csv/parse.d.ts +40 -0
- package/dist/types/src/translate/formula.d.ts +14 -0
- package/dist/types/src/types.d.ts +42 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# excel-to-formula-engine
|
|
2
2
|
|
|
3
|
-
Convert
|
|
3
|
+
Convert a spreadsheet file — `.xlsx`, `.csv` or `.tsv` — into
|
|
4
|
+
[`@ricsam/formula-engine`](https://github.com/ricsam/formula-engine)
|
|
4
5
|
`WorkbookData`, in the browser.
|
|
5
6
|
|
|
6
7
|
```ts
|
|
@@ -22,6 +23,54 @@ external.
|
|
|
22
23
|
bun add @ricsam/excel-to-formula-engine @ricsam/formula-engine
|
|
23
24
|
```
|
|
24
25
|
|
|
26
|
+
## Delimited text
|
|
27
|
+
|
|
28
|
+
CSV and TSV go through `csvToFormulaEngine`, or through
|
|
29
|
+
`spreadsheetToFormulaEngine` when you do not know which kind of file you have —
|
|
30
|
+
a drop target, typically. That one settles the format from the bytes rather than
|
|
31
|
+
the file name, since `.xlsx` is a ZIP and delimited text is not, so a workbook
|
|
32
|
+
saved with a `.csv` extension still reads correctly.
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import {
|
|
36
|
+
csvToFormulaEngine,
|
|
37
|
+
spreadsheetToFormulaEngine
|
|
38
|
+
} from "@ricsam/excel-to-formula-engine";
|
|
39
|
+
|
|
40
|
+
// Delimiter is detected: comma, tab, semicolon or pipe.
|
|
41
|
+
const data = await csvToFormulaEngine(file);
|
|
42
|
+
|
|
43
|
+
// Or let it work out which kind of file this is.
|
|
44
|
+
const data = await spreadsheetToFormulaEngine(file);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Parsing follows RFC 4180, so a quoted field may contain the delimiter, doubled
|
|
48
|
+
quotes, and line breaks. The UTF-8 BOM Excel writes on every CSV export is
|
|
49
|
+
stripped — left in, it becomes an invisible part of the first header cell.
|
|
50
|
+
|
|
51
|
+
### What a bare string becomes
|
|
52
|
+
|
|
53
|
+
A CSV carries no types, so every cell is a judgement call. The rules err toward
|
|
54
|
+
keeping data recoverable:
|
|
55
|
+
|
|
56
|
+
| Field | Becomes | Why |
|
|
57
|
+
| --- | --- | --- |
|
|
58
|
+
| `42`, `-3.5`, `1.50`, `1e3` | number | Plain decimals. |
|
|
59
|
+
| `TRUE` / `false` | boolean | Any case, as in a spreadsheet. |
|
|
60
|
+
| `=B2*C2` | formula | What a spreadsheet does with the same file. |
|
|
61
|
+
| `007`, `01234` | **text** | Part numbers and postcodes — the mangling Excel is notorious for. |
|
|
62
|
+
| `12345678901234567890` | **text** | Beyond float precision; converting would silently change it. |
|
|
63
|
+
| `1,000`, `$5`, `50%` | **text** | Converting would drop the formatting. |
|
|
64
|
+
| `0x1f`, `Infinity` | **text** | `Number()` accepts these; a spreadsheet does not. |
|
|
65
|
+
|
|
66
|
+
Empty fields are left out rather than stored as empty strings, which is what
|
|
67
|
+
keeps a sparse sheet sparse.
|
|
68
|
+
|
|
69
|
+
There is deliberately no option to keep an `=`-leading field as text: the engine
|
|
70
|
+
stores a formula as a string beginning with `=` and has no escape for text that
|
|
71
|
+
merely looks like one, so such an option could only be honoured by altering the
|
|
72
|
+
value.
|
|
73
|
+
|
|
25
74
|
## Usage
|
|
26
75
|
|
|
27
76
|
`excelToFormulaEngine` accepts a `Blob` or `File` straight from a file input, or
|
package/dist/index.cjs
CHANGED
|
@@ -41,7 +41,11 @@ var exports_excel_to_formula_engine = {};
|
|
|
41
41
|
__export(exports_excel_to_formula_engine, {
|
|
42
42
|
columnIndexToLabel: () => columnIndexToLabel,
|
|
43
43
|
columnLabelToIndex: () => columnLabelToIndex,
|
|
44
|
+
convertDelimitedText: () => convertDelimitedText,
|
|
44
45
|
convertWorkbook: () => convertWorkbook,
|
|
46
|
+
csvToFormulaEngine: () => csvToFormulaEngine,
|
|
47
|
+
csvToFormulaEngineDetailed: () => csvToFormulaEngineDetailed,
|
|
48
|
+
detectDelimiter: () => detectDelimiter,
|
|
45
49
|
excelSerialToIso: () => excelSerialToIso,
|
|
46
50
|
excelToFormulaEngine: () => excelToFormulaEngine,
|
|
47
51
|
excelToFormulaEngineDetailed: () => excelToFormulaEngineDetailed,
|
|
@@ -49,6 +53,10 @@ __export(exports_excel_to_formula_engine, {
|
|
|
49
53
|
offsetFormulaReferences: () => offsetFormulaReferences,
|
|
50
54
|
parseA1: () => parseA1,
|
|
51
55
|
parseA1Range: () => parseA1Range,
|
|
56
|
+
parseDelimitedText: () => parseDelimitedText,
|
|
57
|
+
spreadsheetToFormulaEngine: () => spreadsheetToFormulaEngine,
|
|
58
|
+
spreadsheetToFormulaEngineDetailed: () => spreadsheetToFormulaEngineDetailed,
|
|
59
|
+
stripBom: () => stripBom,
|
|
52
60
|
toA1: () => toA1,
|
|
53
61
|
translateFormula: () => translateFormula
|
|
54
62
|
});
|
|
@@ -961,12 +969,14 @@ function getSupportedFunctions() {
|
|
|
961
969
|
return supportedFunctionNames;
|
|
962
970
|
}
|
|
963
971
|
var XLFN_PREFIX = /_xlfn\.(_xlws\.)?/g;
|
|
964
|
-
var IMPLICIT_INTERSECTION = /(?<![\w$"'\]])@(?=[A-Za-z_\\])/g;
|
|
972
|
+
var IMPLICIT_INTERSECTION = /(?<![\w$"'\]\[])@(?=[A-Za-z_\\])/g;
|
|
973
|
+
var THIS_ROW_REFERENCE = /(?:'((?:[^']|'')+)'|([A-Za-z_\\][\w.\\]*))?\[\[#This ?Row\],\s*\[([^\]]*)\](?:\s*:\s*\[([^\]]*)\])?\]/gi;
|
|
965
974
|
function translateFormula(rawFormula, options = {}) {
|
|
966
975
|
const diagnostics = [];
|
|
967
976
|
let body = rawFormula.startsWith("=") ? rawFormula.slice(1) : rawFormula;
|
|
968
977
|
body = body.replace(XLFN_PREFIX, "");
|
|
969
978
|
body = body.replace(IMPLICIT_INTERSECTION, "");
|
|
979
|
+
body = contractThisRowReferences(body, options.containingTableName);
|
|
970
980
|
for (const name of findUnsupportedFunctions(body)) {
|
|
971
981
|
diagnostics.push({
|
|
972
982
|
severity: "warning",
|
|
@@ -1079,6 +1089,22 @@ function columnIndexToLabel2(index) {
|
|
|
1079
1089
|
}
|
|
1080
1090
|
return label;
|
|
1081
1091
|
}
|
|
1092
|
+
function contractThisRowReferences(formulaBody, containingTableName) {
|
|
1093
|
+
return formulaBody.replace(THIS_ROW_REFERENCE, (whole, quotedTable, bareTable, startCol, endCol) => {
|
|
1094
|
+
const tableName = quotedTable ? String(quotedTable).replace(/''/g, "'") : bareTable ? String(bareTable) : undefined;
|
|
1095
|
+
const columns = endCol === undefined ? `[${startCol}]` : `[${startCol}]:[${endCol}]`;
|
|
1096
|
+
const isPlainSingleColumn = endCol === undefined && /^[^[\]@#'"]+$/.test(String(startCol));
|
|
1097
|
+
const reference = isPlainSingleColumn ? `@${startCol}` : `@${columns}`;
|
|
1098
|
+
if (tableName && containingTableName && tableName.toLowerCase() === containingTableName.toLowerCase()) {
|
|
1099
|
+
return `[${reference}]`;
|
|
1100
|
+
}
|
|
1101
|
+
if (!tableName) {
|
|
1102
|
+
return `[${reference}]`;
|
|
1103
|
+
}
|
|
1104
|
+
const quoted = quotedTable ? `'${String(quotedTable)}'` : tableName;
|
|
1105
|
+
return `${quoted}[${reference}]`;
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1082
1108
|
|
|
1083
1109
|
// src/translate/conditional-format.ts
|
|
1084
1110
|
var import_formula_engine2 = require("@ricsam/formula-engine");
|
|
@@ -1309,6 +1335,9 @@ async function convertWorkbook(source, options = {}) {
|
|
|
1309
1335
|
for (const entry of selected) {
|
|
1310
1336
|
const worksheet = parseWorksheet(pkg, entry.partPath, sharedStrings);
|
|
1311
1337
|
importedSheetNames.add(entry.name);
|
|
1338
|
+
const sheetTables = importTables ? parseTables(pkg, entry.partPath) : [];
|
|
1339
|
+
const tableRanges = sheetTables.map((table) => ({ name: table.displayName, range: parseA1Range(table.ref) })).filter((entryTable) => entryTable.range !== undefined);
|
|
1340
|
+
const tableAt = (colIndex, rowIndex) => tableRanges.find(({ range }) => colIndex >= range.start.colIndex && colIndex <= range.end.colIndex && rowIndex >= range.start.rowIndex && rowIndex <= range.end.rowIndex)?.name;
|
|
1312
1341
|
const content = new Map;
|
|
1313
1342
|
const cellMetadata = new Map;
|
|
1314
1343
|
const styleGroups = new Map;
|
|
@@ -1318,7 +1347,8 @@ async function convertWorkbook(source, options = {}) {
|
|
|
1318
1347
|
convertDates,
|
|
1319
1348
|
date1904,
|
|
1320
1349
|
sheetName: entry.name,
|
|
1321
|
-
diagnostics
|
|
1350
|
+
diagnostics,
|
|
1351
|
+
containingTableName: tableAt(cell.colIndex, cell.rowIndex)
|
|
1322
1352
|
});
|
|
1323
1353
|
if (value !== undefined) {
|
|
1324
1354
|
content.set(cell.reference, value);
|
|
@@ -1373,7 +1403,7 @@ async function convertWorkbook(source, options = {}) {
|
|
|
1373
1403
|
}
|
|
1374
1404
|
}
|
|
1375
1405
|
if (importTables) {
|
|
1376
|
-
for (const table of
|
|
1406
|
+
for (const table of sheetTables) {
|
|
1377
1407
|
const converted = convertTable(table, entry.name, diagnostics);
|
|
1378
1408
|
if (converted) {
|
|
1379
1409
|
tables.push(converted);
|
|
@@ -1423,7 +1453,8 @@ function cellValue(cell, format, context) {
|
|
|
1423
1453
|
const body = cell.formulaOffset ? offsetFormulaReferences(cell.formula, cell.formulaOffset.cols, cell.formulaOffset.rows) : cell.formula;
|
|
1424
1454
|
const translated = translateFormula(body, {
|
|
1425
1455
|
sheetName: context.sheetName,
|
|
1426
|
-
cellReference: cell.reference
|
|
1456
|
+
cellReference: cell.reference,
|
|
1457
|
+
containingTableName: context.containingTableName
|
|
1427
1458
|
});
|
|
1428
1459
|
context.diagnostics.push(...translated.diagnostics);
|
|
1429
1460
|
return translated.formula;
|
|
@@ -1569,6 +1600,238 @@ function coalesceCellsIntoAreas(sheetName, cells) {
|
|
|
1569
1600
|
return areas;
|
|
1570
1601
|
}
|
|
1571
1602
|
|
|
1603
|
+
// src/csv/parse.ts
|
|
1604
|
+
var CANDIDATE_DELIMITERS = [",", "\t", ";", "|"];
|
|
1605
|
+
function parseDelimitedText(text, delimiter) {
|
|
1606
|
+
const rows = [];
|
|
1607
|
+
let row = [];
|
|
1608
|
+
let field = "";
|
|
1609
|
+
let inQuotes = false;
|
|
1610
|
+
let fieldWasQuoted = false;
|
|
1611
|
+
let rowHasContent = false;
|
|
1612
|
+
const endField = () => {
|
|
1613
|
+
row.push(field);
|
|
1614
|
+
field = "";
|
|
1615
|
+
fieldWasQuoted = false;
|
|
1616
|
+
rowHasContent = true;
|
|
1617
|
+
};
|
|
1618
|
+
const endRow = () => {
|
|
1619
|
+
endField();
|
|
1620
|
+
rows.push(row);
|
|
1621
|
+
row = [];
|
|
1622
|
+
rowHasContent = false;
|
|
1623
|
+
};
|
|
1624
|
+
for (let i = 0;i < text.length; i++) {
|
|
1625
|
+
const char = text[i];
|
|
1626
|
+
if (inQuotes) {
|
|
1627
|
+
if (char === '"') {
|
|
1628
|
+
if (text[i + 1] === '"') {
|
|
1629
|
+
field += '"';
|
|
1630
|
+
i++;
|
|
1631
|
+
} else {
|
|
1632
|
+
inQuotes = false;
|
|
1633
|
+
}
|
|
1634
|
+
} else {
|
|
1635
|
+
field += char;
|
|
1636
|
+
}
|
|
1637
|
+
continue;
|
|
1638
|
+
}
|
|
1639
|
+
if (char === '"' && field.length === 0) {
|
|
1640
|
+
inQuotes = true;
|
|
1641
|
+
fieldWasQuoted = true;
|
|
1642
|
+
continue;
|
|
1643
|
+
}
|
|
1644
|
+
if (char === delimiter) {
|
|
1645
|
+
endField();
|
|
1646
|
+
continue;
|
|
1647
|
+
}
|
|
1648
|
+
if (char === "\r") {
|
|
1649
|
+
if (text[i + 1] === `
|
|
1650
|
+
`)
|
|
1651
|
+
i++;
|
|
1652
|
+
endRow();
|
|
1653
|
+
continue;
|
|
1654
|
+
}
|
|
1655
|
+
if (char === `
|
|
1656
|
+
`) {
|
|
1657
|
+
endRow();
|
|
1658
|
+
continue;
|
|
1659
|
+
}
|
|
1660
|
+
field += char;
|
|
1661
|
+
}
|
|
1662
|
+
if (field.length > 0 || fieldWasQuoted || rowHasContent) {
|
|
1663
|
+
endRow();
|
|
1664
|
+
}
|
|
1665
|
+
return rows;
|
|
1666
|
+
}
|
|
1667
|
+
function detectDelimiter(text, candidates = CANDIDATE_DELIMITERS) {
|
|
1668
|
+
const lines = takeSampleLines(text, 20);
|
|
1669
|
+
if (lines.length === 0)
|
|
1670
|
+
return ",";
|
|
1671
|
+
let best;
|
|
1672
|
+
for (const delimiter of candidates) {
|
|
1673
|
+
const counts = lines.map((line) => countOutsideQuotes(line, delimiter));
|
|
1674
|
+
const present = counts.filter((count) => count > 0);
|
|
1675
|
+
if (present.length === 0)
|
|
1676
|
+
continue;
|
|
1677
|
+
const first = counts[0];
|
|
1678
|
+
const consistent = counts.filter((count) => count === first).length;
|
|
1679
|
+
const score = consistent / counts.length + Math.min(first, 50) / 1000;
|
|
1680
|
+
if (!best || score > best.score) {
|
|
1681
|
+
best = { delimiter, score };
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
return best?.delimiter ?? ",";
|
|
1685
|
+
}
|
|
1686
|
+
function takeSampleLines(text, limit) {
|
|
1687
|
+
const lines = [];
|
|
1688
|
+
let current = "";
|
|
1689
|
+
let inQuotes = false;
|
|
1690
|
+
for (let i = 0;i < text.length && lines.length < limit; i++) {
|
|
1691
|
+
const char = text[i];
|
|
1692
|
+
if (char === '"') {
|
|
1693
|
+
if (inQuotes && text[i + 1] === '"') {
|
|
1694
|
+
current += '""';
|
|
1695
|
+
i++;
|
|
1696
|
+
continue;
|
|
1697
|
+
}
|
|
1698
|
+
inQuotes = !inQuotes;
|
|
1699
|
+
current += char;
|
|
1700
|
+
continue;
|
|
1701
|
+
}
|
|
1702
|
+
if (!inQuotes && (char === `
|
|
1703
|
+
` || char === "\r")) {
|
|
1704
|
+
if (char === "\r" && text[i + 1] === `
|
|
1705
|
+
`)
|
|
1706
|
+
i++;
|
|
1707
|
+
if (current.trim().length > 0)
|
|
1708
|
+
lines.push(current);
|
|
1709
|
+
current = "";
|
|
1710
|
+
continue;
|
|
1711
|
+
}
|
|
1712
|
+
current += char;
|
|
1713
|
+
}
|
|
1714
|
+
if (current.trim().length > 0 && lines.length < limit)
|
|
1715
|
+
lines.push(current);
|
|
1716
|
+
return lines;
|
|
1717
|
+
}
|
|
1718
|
+
function countOutsideQuotes(line, delimiter) {
|
|
1719
|
+
let count = 0;
|
|
1720
|
+
let inQuotes = false;
|
|
1721
|
+
for (let i = 0;i < line.length; i++) {
|
|
1722
|
+
const char = line[i];
|
|
1723
|
+
if (char === '"') {
|
|
1724
|
+
if (inQuotes && line[i + 1] === '"') {
|
|
1725
|
+
i++;
|
|
1726
|
+
continue;
|
|
1727
|
+
}
|
|
1728
|
+
inQuotes = !inQuotes;
|
|
1729
|
+
continue;
|
|
1730
|
+
}
|
|
1731
|
+
if (!inQuotes && char === delimiter)
|
|
1732
|
+
count++;
|
|
1733
|
+
}
|
|
1734
|
+
return count;
|
|
1735
|
+
}
|
|
1736
|
+
function stripBom(text) {
|
|
1737
|
+
return text.charCodeAt(0) === 65279 ? text.slice(1) : text;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// src/csv/convert.ts
|
|
1741
|
+
var MAX_CELLS = 2000000;
|
|
1742
|
+
function convertDelimitedText(text, options = {}) {
|
|
1743
|
+
const {
|
|
1744
|
+
sheetName = "Sheet1",
|
|
1745
|
+
fileName,
|
|
1746
|
+
parseNumbers = true,
|
|
1747
|
+
parseBooleans = true,
|
|
1748
|
+
trimFields = false
|
|
1749
|
+
} = options;
|
|
1750
|
+
const diagnostics = [];
|
|
1751
|
+
const body = stripBom(text);
|
|
1752
|
+
const delimiter = options.delimiter ?? detectDelimiter(body);
|
|
1753
|
+
const rows = parseDelimitedText(body, delimiter);
|
|
1754
|
+
const widest = rows.reduce((max, row) => Math.max(max, row.length), 0);
|
|
1755
|
+
if (rows.length * widest > MAX_CELLS) {
|
|
1756
|
+
throw new Error(`The file has about ${rows.length * widest} cells, more than the ` + `${MAX_CELLS} this importer will load at once.`);
|
|
1757
|
+
}
|
|
1758
|
+
const content = new Map;
|
|
1759
|
+
for (let rowIndex = 0;rowIndex < rows.length; rowIndex++) {
|
|
1760
|
+
const row = rows[rowIndex];
|
|
1761
|
+
for (let colIndex = 0;colIndex < row.length; colIndex++) {
|
|
1762
|
+
const raw = trimFields ? row[colIndex].trim() : row[colIndex];
|
|
1763
|
+
if (raw.length === 0)
|
|
1764
|
+
continue;
|
|
1765
|
+
const reference = `${columnIndexToLabel(colIndex)}${rowIndex + 1}`;
|
|
1766
|
+
content.set(reference, toCellValue(raw, {
|
|
1767
|
+
parseNumbers,
|
|
1768
|
+
parseBooleans,
|
|
1769
|
+
sheetName,
|
|
1770
|
+
reference,
|
|
1771
|
+
diagnostics
|
|
1772
|
+
}));
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
const data = {
|
|
1776
|
+
sheets: [{ name: sheetName, content }],
|
|
1777
|
+
workbookMetadata: {
|
|
1778
|
+
fileName,
|
|
1779
|
+
sheetOrder: [sheetName],
|
|
1780
|
+
diagnostics: diagnostics.length > 0 ? diagnostics : undefined
|
|
1781
|
+
}
|
|
1782
|
+
};
|
|
1783
|
+
return { data, diagnostics };
|
|
1784
|
+
}
|
|
1785
|
+
function toCellValue(raw, context) {
|
|
1786
|
+
if (raw.startsWith("=") && raw.length > 1) {
|
|
1787
|
+
const translated = translateFormula(raw, {
|
|
1788
|
+
sheetName: context.sheetName,
|
|
1789
|
+
cellReference: context.reference
|
|
1790
|
+
});
|
|
1791
|
+
context.diagnostics.push(...translated.diagnostics);
|
|
1792
|
+
return translated.formula;
|
|
1793
|
+
}
|
|
1794
|
+
if (context.parseBooleans) {
|
|
1795
|
+
const upper = raw.toUpperCase();
|
|
1796
|
+
if (upper === "TRUE")
|
|
1797
|
+
return true;
|
|
1798
|
+
if (upper === "FALSE")
|
|
1799
|
+
return false;
|
|
1800
|
+
}
|
|
1801
|
+
if (context.parseNumbers) {
|
|
1802
|
+
const number = toNumber(raw);
|
|
1803
|
+
if (number !== undefined)
|
|
1804
|
+
return number;
|
|
1805
|
+
}
|
|
1806
|
+
return raw;
|
|
1807
|
+
}
|
|
1808
|
+
function toNumber(raw) {
|
|
1809
|
+
const text = raw.trim();
|
|
1810
|
+
if (text.length === 0)
|
|
1811
|
+
return;
|
|
1812
|
+
if (!/^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/.test(text)) {
|
|
1813
|
+
return;
|
|
1814
|
+
}
|
|
1815
|
+
const digits = text.replace(/^[+-]/, "");
|
|
1816
|
+
if (/^0\d/.test(digits))
|
|
1817
|
+
return;
|
|
1818
|
+
const value = Number(text);
|
|
1819
|
+
if (!Number.isFinite(value))
|
|
1820
|
+
return;
|
|
1821
|
+
if (!text.includes(".") && !/[eE]/.test(text) && !Number.isSafeInteger(value)) {
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
return value;
|
|
1825
|
+
}
|
|
1826
|
+
async function readTextFrom(source) {
|
|
1827
|
+
if (typeof source === "string")
|
|
1828
|
+
return source;
|
|
1829
|
+
if (source instanceof Uint8Array || source instanceof ArrayBuffer) {
|
|
1830
|
+
return new TextDecoder("utf-8").decode(source);
|
|
1831
|
+
}
|
|
1832
|
+
return source.text();
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1572
1835
|
// index.ts
|
|
1573
1836
|
async function excelToFormulaEngine(source, options) {
|
|
1574
1837
|
const { data } = await convertWorkbook(source, withFileName(source, options));
|
|
@@ -1584,5 +1847,46 @@ function withFileName(source, options) {
|
|
|
1584
1847
|
const name = typeof File !== "undefined" && source instanceof File ? source.name : undefined;
|
|
1585
1848
|
return name ? { ...options, fileName: name } : { ...options };
|
|
1586
1849
|
}
|
|
1850
|
+
async function csvToFormulaEngine(source, options) {
|
|
1851
|
+
const { data } = await csvToFormulaEngineDetailed(source, options);
|
|
1852
|
+
return data;
|
|
1853
|
+
}
|
|
1854
|
+
async function csvToFormulaEngineDetailed(source, options) {
|
|
1855
|
+
const text = await readTextFrom(source);
|
|
1856
|
+
const resolved = withCsvFileName(source, options);
|
|
1857
|
+
return convertDelimitedText(text, resolved);
|
|
1858
|
+
}
|
|
1859
|
+
async function spreadsheetToFormulaEngine(source, options) {
|
|
1860
|
+
const { data } = await spreadsheetToFormulaEngineDetailed(source, options);
|
|
1861
|
+
return data;
|
|
1862
|
+
}
|
|
1863
|
+
async function spreadsheetToFormulaEngineDetailed(source, options) {
|
|
1864
|
+
const bytes = await toBytes2(source);
|
|
1865
|
+
if (looksLikeZip(bytes)) {
|
|
1866
|
+
return convertWorkbook(bytes, withFileName(source, options));
|
|
1867
|
+
}
|
|
1868
|
+
return csvToFormulaEngineDetailed(bytes, withCsvFileName(source, options));
|
|
1869
|
+
}
|
|
1870
|
+
function looksLikeZip(bytes) {
|
|
1871
|
+
return bytes.length >= 4 && bytes[0] === 80 && bytes[1] === 75 && bytes[2] === 3 && bytes[3] === 4;
|
|
1872
|
+
}
|
|
1873
|
+
async function toBytes2(source) {
|
|
1874
|
+
if (source instanceof Uint8Array)
|
|
1875
|
+
return source;
|
|
1876
|
+
if (source instanceof ArrayBuffer)
|
|
1877
|
+
return new Uint8Array(source);
|
|
1878
|
+
return new Uint8Array(await source.arrayBuffer());
|
|
1879
|
+
}
|
|
1880
|
+
function withCsvFileName(source, options) {
|
|
1881
|
+
const name = typeof File !== "undefined" && source instanceof File ? source.name : undefined;
|
|
1882
|
+
const fileName = options?.fileName ?? name;
|
|
1883
|
+
if (!fileName)
|
|
1884
|
+
return { ...options };
|
|
1885
|
+
return {
|
|
1886
|
+
...options,
|
|
1887
|
+
fileName,
|
|
1888
|
+
sheetName: options?.sheetName ?? (fileName.replace(/\.(csv|tsv|tab|txt)$/i, "").trim() || "Sheet1")
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1587
1891
|
|
|
1588
|
-
//# debugId=
|
|
1892
|
+
//# debugId=E62FBA8DCEBAE6FE64756E2164756E21
|