@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 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","cells":{
162
- "A1":{"value":"Tổng","style":{"fontBold":true}},
163
- "B1":{"value":1234567,"numFmt":"#,##0"},
164
- "B2":{"formula":"B1*0.1","numFmt":"#,##0"}}}]}' # unknown cell keys are rejected, not ignored
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'
@@ -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
- 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 === "xdr:twoCellAnchor" || tagName === "xdr:oneCellAnchor" || tagName === "dataValidation" || tagName === "filterColumn" || tagName === "filter" || tagName === "brk" || tagName === "ext",
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) return emptySheet("Sheet");
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) return { sheets: [], activeSheetIndex: 0, date1904: false, namedRanges: /* @__PURE__ */ new Map(), printTitlesBySheet: /* @__PURE__ */ new Map(), fullCalcOnLoad: false };
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
- if (wbEntry) {
66240
- const wbDoc = parseXmlDoc(decodeUtf8(wbEntry));
66241
- const wbCacheMap = extractWorkbookPivotCaches(wbDoc);
66242
- for (const [cacheId, rId] of wbCacheMap) {
66243
- const cacheTarget = wbRels.get(rId);
66244
- if (!cacheTarget) continue;
66245
- const cachePath = cacheTarget.startsWith("/") ? cacheTarget.slice(1) : `xl/${cacheTarget}`;
66246
- const cacheEntry = findEntry(zip, cachePath);
66247
- if (!cacheEntry) continue;
66248
- try {
66249
- pivotCaches.set(
66250
- cacheId,
66251
- parsePivotCacheDefinitionXml(decodeUtf8(cacheEntry), cacheId, parseXmlDoc)
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
  }