@lotics/cli 0.111.0 → 0.113.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 +7 -4
- package/dist/render_page.js +49 -22
- package/dist/src/cli.js +616 -474
- package/docs/cli_reference.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -156,12 +156,15 @@ The CLI bundles Lotics's own xlsx and docx engines so you can read, write, and e
|
|
|
156
156
|
```bash
|
|
157
157
|
# .xlsx
|
|
158
158
|
lotics xlsx read ./report.xlsx # → JSON of sheets, cells, merges
|
|
159
|
+
lotics xlsx read ./report.xlsx --with-format # …plus each cell's resolved style
|
|
159
160
|
lotics xlsx write ./out.xlsx '{"sheets":[{"name":"S1","cells":{"A1":"Hi","B1":42}}]}'
|
|
160
161
|
# A cell is a bare value, or an object with numFmt / style / formula:
|
|
161
|
-
lotics xlsx write ./out.xlsx '{"sheets":[{"name":"S1",
|
|
162
|
-
"
|
|
163
|
-
"
|
|
164
|
-
|
|
162
|
+
lotics xlsx write ./out.xlsx '{"sheets":[{"name":"S1",
|
|
163
|
+
"colWidths":{"A":34,"B":14}, "rowHeights":{"1":28},
|
|
164
|
+
"cells":{
|
|
165
|
+
"A1":{"value":"Tổng","style":{"fontBold":true}},
|
|
166
|
+
"B1":{"value":1234567,"numFmt":"#,##0"},
|
|
167
|
+
"B2":{"formula":"B1*0.1","numFmt":"#,##0"}}}]}' # unknown keys are rejected, not ignored
|
|
165
168
|
lotics xlsx set-cell ./report.xlsx 'Sheet1!A1' '=SUM(B:B)'
|
|
166
169
|
lotics xlsx add-sheet ./report.xlsx 'Summary'
|
|
167
170
|
lotics xlsx merge ./report.xlsx 'Sheet1!A1:C1'
|
package/dist/render_page.js
CHANGED
|
@@ -50248,6 +50248,10 @@ ${e.toString()}`);
|
|
|
50248
50248
|
const { row, col } = unpackRowCol(packed);
|
|
50249
50249
|
return rowColToRef(row, col);
|
|
50250
50250
|
}
|
|
50251
|
+
function stripNsPrefix(tag) {
|
|
50252
|
+
const colon = tag.indexOf(":");
|
|
50253
|
+
return colon === -1 ? tag : tag.slice(colon + 1);
|
|
50254
|
+
}
|
|
50251
50255
|
|
|
50252
50256
|
// ../xlsx/node_modules/fast-xml-parser/src/util.js
|
|
50253
50257
|
var nameStartChar2 = ":A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
|
|
@@ -52246,6 +52250,16 @@ ${e.toString()}`);
|
|
|
52246
52250
|
return { type: "plain", text: displayValue };
|
|
52247
52251
|
}
|
|
52248
52252
|
|
|
52253
|
+
// ../xlsx/src/malformed_part_error.ts
|
|
52254
|
+
var MalformedPartError = class extends Error {
|
|
52255
|
+
constructor(reason) {
|
|
52256
|
+
super(
|
|
52257
|
+
`${reason} \u2014 the file is not a readable spreadsheet. If it opens in Excel, the reader is at fault: report the producer that wrote it.`
|
|
52258
|
+
);
|
|
52259
|
+
this.name = "MalformedPartError";
|
|
52260
|
+
}
|
|
52261
|
+
};
|
|
52262
|
+
|
|
52249
52263
|
// ../../node_modules/fflate/esm/browser.js
|
|
52250
52264
|
var u8 = Uint8Array;
|
|
52251
52265
|
var u16 = Uint16Array;
|
|
@@ -52846,7 +52860,11 @@ ${e.toString()}`);
|
|
|
52846
52860
|
ignoreAttributes: false,
|
|
52847
52861
|
attributeNamePrefix: "@_",
|
|
52848
52862
|
processEntities: false,
|
|
52849
|
-
|
|
52863
|
+
// A producer may bind spreadsheetml to a prefix (`<x:worksheet>`) instead of
|
|
52864
|
+
// making it the default namespace. The prefix carries no meaning, so it is
|
|
52865
|
+
// dropped before anything looks a tag up — see `stripNsPrefix`.
|
|
52866
|
+
transformTagName: stripNsPrefix,
|
|
52867
|
+
isArray: (tagName) => tagName === "row" || tagName === "c" || tagName === "col" || tagName === "mergeCell" || tagName === "conditionalFormatting" || tagName === "cfRule" || tagName === "cfvo" || tagName === "color" || tagName === "hyperlink" || tagName === "Relationship" || tagName === "sheetView" || tagName === "dataValidation" || tagName === "filterColumn" || tagName === "filter" || tagName === "brk" || tagName === "ext",
|
|
52850
52868
|
// Preserve whitespace in cell values
|
|
52851
52869
|
trimValues: false
|
|
52852
52870
|
});
|
|
@@ -52865,7 +52883,7 @@ ${e.toString()}`);
|
|
|
52865
52883
|
function parseSheet(sheetXml, sheetRelsXml, styles, sharedStrings, theme, indexedColors, zipEntries, date1904, maxRows = MAX_ROWS_PER_SHEET) {
|
|
52866
52884
|
const doc3 = xmlParser2.parse(sheetXml);
|
|
52867
52885
|
const worksheet = doc3?.["worksheet"];
|
|
52868
|
-
if (!worksheet)
|
|
52886
|
+
if (!worksheet) throw new MalformedPartError("A worksheet part has no <worksheet> root");
|
|
52869
52887
|
const rels = sheetRelsXml ? parseSheetRels(sheetRelsXml) : /* @__PURE__ */ new Map();
|
|
52870
52888
|
const sheetFormatPr = worksheet["sheetFormatPr"];
|
|
52871
52889
|
const defaultRowHeight = sheetFormatPr?.["@_defaultRowHeight"] ? parseFloat(sheetFormatPr["@_defaultRowHeight"]) : 15;
|
|
@@ -64560,6 +64578,10 @@ ${e.toString()}`);
|
|
|
64560
64578
|
var xmlParser3 = new XMLParser2({
|
|
64561
64579
|
ignoreAttributes: false,
|
|
64562
64580
|
attributeNamePrefix: "@_",
|
|
64581
|
+
// A producer may bind spreadsheetml to a prefix (`<x:styleSheet>`) rather than
|
|
64582
|
+
// making it the default namespace; the prefix carries no meaning, so it is
|
|
64583
|
+
// dropped before anything looks a tag up — see `stripNsPrefix`.
|
|
64584
|
+
transformTagName: stripNsPrefix,
|
|
64563
64585
|
processEntities: false,
|
|
64564
64586
|
isArray: (tagName) => tagName === "numFmt" || tagName === "font" || tagName === "fill" || tagName === "border" || tagName === "xf" || tagName === "rgbColor" || tagName === "stop"
|
|
64565
64587
|
});
|
|
@@ -64893,6 +64915,10 @@ ${e.toString()}`);
|
|
|
64893
64915
|
var xmlParser4 = new XMLParser2({
|
|
64894
64916
|
ignoreAttributes: false,
|
|
64895
64917
|
attributeNamePrefix: "@_",
|
|
64918
|
+
// A producer may bind spreadsheetml to a prefix (`<x:workbook>`) rather than
|
|
64919
|
+
// making it the default namespace; the prefix carries no meaning, so it is
|
|
64920
|
+
// dropped before anything looks a tag up — see `stripNsPrefix`.
|
|
64921
|
+
transformTagName: stripNsPrefix,
|
|
64896
64922
|
// Shared-string and workbook text is textual BY DEFINITION (tax IDs, phone
|
|
64897
64923
|
// numbers, account codes, sheet/defined names). fast-xml-parser's default tag
|
|
64898
64924
|
// coercion would parse `<t>0312345678</t>` as the number 312345678 — silently
|
|
@@ -64906,7 +64932,7 @@ ${e.toString()}`);
|
|
|
64906
64932
|
function parseWorkbook(workbookXml) {
|
|
64907
64933
|
const doc3 = xmlParser4.parse(workbookXml);
|
|
64908
64934
|
const wb = doc3?.["workbook"];
|
|
64909
|
-
if (!wb)
|
|
64935
|
+
if (!wb) throw new MalformedPartError("xl/workbook.xml has no <workbook> root");
|
|
64910
64936
|
const sheetsEl = wb["sheets"];
|
|
64911
64937
|
const sheetArr = sheetsEl?.["sheet"];
|
|
64912
64938
|
const sheets = (sheetArr ?? []).map((s) => ({
|
|
@@ -66220,9 +66246,7 @@ ${e.toString()}`);
|
|
|
66220
66246
|
const stylesEntry = findEntry(zip, "xl/styles.xml");
|
|
66221
66247
|
const styles = stylesEntry ? parseStyles(decodeUtf8(stylesEntry), theme) : emptyStyleResolver();
|
|
66222
66248
|
const wbEntry = findEntry(zip, "xl/workbook.xml");
|
|
66223
|
-
if (!wbEntry)
|
|
66224
|
-
return { sheets: [emptySheet("Sheet1")], activeSheetIndex: 0 };
|
|
66225
|
-
}
|
|
66249
|
+
if (!wbEntry) throw new MalformedPartError("The package has no xl/workbook.xml");
|
|
66226
66250
|
const workbookInfo = parseWorkbook(decodeUtf8(wbEntry));
|
|
66227
66251
|
const wbRelsEntry = findEntry(zip, "xl/_rels/workbook.xml.rels");
|
|
66228
66252
|
const wbRels = wbRelsEntry ? parseWorkbookRels(decodeUtf8(wbRelsEntry)) : /* @__PURE__ */ new Map();
|
|
@@ -66236,22 +66260,20 @@ ${e.toString()}`);
|
|
|
66236
66260
|
});
|
|
66237
66261
|
const parseXmlDoc = (xml) => xmlParser5.parse(xml);
|
|
66238
66262
|
const pivotCaches = /* @__PURE__ */ new Map();
|
|
66239
|
-
|
|
66240
|
-
|
|
66241
|
-
|
|
66242
|
-
|
|
66243
|
-
|
|
66244
|
-
|
|
66245
|
-
|
|
66246
|
-
|
|
66247
|
-
|
|
66248
|
-
|
|
66249
|
-
|
|
66250
|
-
|
|
66251
|
-
|
|
66252
|
-
|
|
66253
|
-
} catch {
|
|
66254
|
-
}
|
|
66263
|
+
const wbDoc = parseXmlDoc(decodeUtf8(wbEntry));
|
|
66264
|
+
const wbCacheMap = extractWorkbookPivotCaches(wbDoc);
|
|
66265
|
+
for (const [cacheId, rId] of wbCacheMap) {
|
|
66266
|
+
const cacheTarget = wbRels.get(rId);
|
|
66267
|
+
if (!cacheTarget) continue;
|
|
66268
|
+
const cachePath = cacheTarget.startsWith("/") ? cacheTarget.slice(1) : `xl/${cacheTarget}`;
|
|
66269
|
+
const cacheEntry = findEntry(zip, cachePath);
|
|
66270
|
+
if (!cacheEntry) continue;
|
|
66271
|
+
try {
|
|
66272
|
+
pivotCaches.set(
|
|
66273
|
+
cacheId,
|
|
66274
|
+
parsePivotCacheDefinitionXml(decodeUtf8(cacheEntry), cacheId, parseXmlDoc)
|
|
66275
|
+
);
|
|
66276
|
+
} catch {
|
|
66255
66277
|
}
|
|
66256
66278
|
}
|
|
66257
66279
|
const sheets = [];
|
|
@@ -66375,6 +66397,11 @@ ${e.toString()}`);
|
|
|
66375
66397
|
}
|
|
66376
66398
|
visibleIndex++;
|
|
66377
66399
|
}
|
|
66400
|
+
if (sheets.length === 0 && workbookInfo.sheets.length > 0) {
|
|
66401
|
+
throw new MalformedPartError(
|
|
66402
|
+
`xl/workbook.xml declares ${workbookInfo.sheets.length} sheet(s) and none of their parts could be resolved`
|
|
66403
|
+
);
|
|
66404
|
+
}
|
|
66378
66405
|
if (sheets.length === 0) {
|
|
66379
66406
|
return { sheets: [emptySheet("Sheet1")], activeSheetIndex: 0 };
|
|
66380
66407
|
}
|