@devmm/puredocs-excel 1.0.0 → 1.0.2

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/dist/index.cjs CHANGED
@@ -346,11 +346,22 @@ ${sheetRels}
346
346
  <Relationship Id="rId${sharedId}" Type="${REL_TYPES.sharedStrings}" Target="sharedStrings.xml"/>
347
347
  </Relationships>`;
348
348
  }
349
- function buildWorkbookXml(sheets) {
349
+ function buildWorkbookXml(sheets, definedNames = []) {
350
350
  const sheetElems = sheets.map(({ name, sheetId, relationshipId, state }) => {
351
351
  const stateAttr = state && state !== "visible" ? ` state="${state}"` : "";
352
352
  return ` <sheet name="${escapeAttr(name)}" sheetId="${sheetId}" r:id="${relationshipId}"${stateAttr}/>`;
353
353
  }).join("\n");
354
+ let definedNamesBlock = "";
355
+ if (definedNames.length > 0) {
356
+ const items = definedNames.map(({ name, refersTo, localSheetId }) => {
357
+ const local = localSheetId !== void 0 ? ` localSheetId="${localSheetId}"` : "";
358
+ return ` <definedName name="${escapeAttr(name)}"${local}>${escapeContent(refersTo)}</definedName>`;
359
+ }).join("\n");
360
+ definedNamesBlock = `
361
+ <definedNames>
362
+ ${items}
363
+ </definedNames>`;
364
+ }
354
365
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
355
366
  <workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
356
367
  xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
@@ -361,7 +372,7 @@ function buildWorkbookXml(sheets) {
361
372
  </bookViews>
362
373
  <sheets>
363
374
  ${sheetElems}
364
- </sheets>
375
+ </sheets>${definedNamesBlock}
365
376
  <calcPr calcId="191028" fullCalcOnLoad="1"/>
366
377
  </workbook>`;
367
378
  }
@@ -373,6 +384,9 @@ var EMPTY_WORKSHEET_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"
373
384
  function escapeAttr(s) {
374
385
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
375
386
  }
387
+ function escapeContent(s) {
388
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
389
+ }
376
390
 
377
391
  // src/io/drawing-part.ts
378
392
  var WORKSHEET_DRAWING_REL_ID = "rId1";
@@ -1752,6 +1766,54 @@ var Cell = class {
1752
1766
  __privateGet(this, _data).formula = formula.startsWith("=") ? formula.slice(1) : formula;
1753
1767
  __privateGet(this, _data).rawValue = void 0;
1754
1768
  __privateGet(this, _data).dataType = void 0;
1769
+ __privateGet(this, _data).arrayRef = void 0;
1770
+ return this;
1771
+ }
1772
+ /**
1773
+ * Marks (or unmarks) this cell as a dynamic-array formula anchor. `spillRef`
1774
+ * is the block the array spills over (e.g. "A1:C2"); pass null to revert to a
1775
+ * plain scalar formula. Serialises as `<f t="array" ref="…">`.
1776
+ */
1777
+ setArrayRef(spillRef) {
1778
+ __privateGet(this, _data).arrayRef = spillRef ?? void 0;
1779
+ return this;
1780
+ }
1781
+ /** The dynamic-array spill range, or null if this is not an array anchor. */
1782
+ getArrayRef() {
1783
+ return __privateGet(this, _data).arrayRef ?? null;
1784
+ }
1785
+ /**
1786
+ * Sets the cached/computed result of this cell WITHOUT touching its formula.
1787
+ *
1788
+ * This is what a recalculation engine calls after evaluating the formula: it
1789
+ * stores the value that XLSX serialises into `<v>` alongside the `<f>`. Unlike
1790
+ * setValue(), it does NOT clear the formula, and strings are stored inline
1791
+ * (t="str") the way Excel stores calculated string results — they are not
1792
+ * added to the shared-string table.
1793
+ */
1794
+ setComputedValue(value) {
1795
+ if (value === null || value === void 0) {
1796
+ __privateGet(this, _data).dataType = void 0;
1797
+ __privateGet(this, _data).rawValue = void 0;
1798
+ return this;
1799
+ }
1800
+ if (typeof value === "string") {
1801
+ __privateGet(this, _data).dataType = "str";
1802
+ __privateGet(this, _data).rawValue = value;
1803
+ return this;
1804
+ }
1805
+ if (typeof value === "boolean") {
1806
+ __privateGet(this, _data).dataType = "b";
1807
+ __privateGet(this, _data).rawValue = value ? "1" : "0";
1808
+ return this;
1809
+ }
1810
+ if (value instanceof Date) {
1811
+ __privateGet(this, _data).dataType = "n";
1812
+ __privateGet(this, _data).rawValue = String(toOADate(value));
1813
+ return this;
1814
+ }
1815
+ __privateGet(this, _data).dataType = "n";
1816
+ __privateGet(this, _data).rawValue = String(value);
1755
1817
  return this;
1756
1818
  }
1757
1819
  /** Returns the formula text (without leading '='), or null if no formula. */
@@ -1824,6 +1886,7 @@ var Cell = class {
1824
1886
  __privateGet(this, _data).rawValue = void 0;
1825
1887
  __privateGet(this, _data).dataType = void 0;
1826
1888
  __privateGet(this, _data).formula = void 0;
1889
+ __privateGet(this, _data).arrayRef = void 0;
1827
1890
  }
1828
1891
  /** Clears the cell value, formula, AND style. */
1829
1892
  clearAll() {
@@ -1836,7 +1899,7 @@ var Cell = class {
1836
1899
  const ref = __privateGet(this, _data).reference;
1837
1900
  const style = __privateGet(this, _data).styleIndex > 0 ? ` s="${__privateGet(this, _data).styleIndex}"` : "";
1838
1901
  const type = __privateGet(this, _data).dataType ? ` t="${__privateGet(this, _data).dataType}"` : "";
1839
- const formulaXml = __privateGet(this, _data).formula ? `<f>${escXml(__privateGet(this, _data).formula)}</f>` : "";
1902
+ const formulaXml = __privateGet(this, _data).formula ? __privateGet(this, _data).arrayRef ? `<f t="array" ref="${__privateGet(this, _data).arrayRef}" aca="1" ca="1">${escXml(__privateGet(this, _data).formula)}</f>` : `<f>${escXml(__privateGet(this, _data).formula)}</f>` : "";
1840
1903
  const valueXml = __privateGet(this, _data).rawValue !== void 0 && __privateGet(this, _data).rawValue !== "" ? `<v>${escXml(__privateGet(this, _data).rawValue)}</v>` : "";
1841
1904
  if (!formulaXml && !valueXml && __privateGet(this, _data).styleIndex === 0) return "";
1842
1905
  return `<c r="${ref}"${style}${type}>${formulaXml}${valueXml}</c>`;
@@ -1847,12 +1910,18 @@ var Cell = class {
1847
1910
  const styleIndex = parseInt(el.attributes["s"] ?? "0", 10);
1848
1911
  let rawValue;
1849
1912
  let formula;
1913
+ let arrayRef;
1850
1914
  for (const child of el.children) {
1851
1915
  const tag = child.tagName.replace(/^.*:/, "");
1852
1916
  if (tag === "v") rawValue = child.textContent;
1853
- if (tag === "f") formula = child.textContent;
1917
+ if (tag === "f") {
1918
+ formula = child.textContent;
1919
+ if (child.attributes && child.attributes["t"] === "array" && child.attributes["ref"]) {
1920
+ arrayRef = child.attributes["ref"];
1921
+ }
1922
+ }
1854
1923
  }
1855
- return { reference: ref, dataType, rawValue, formula, styleIndex };
1924
+ return { reference: ref, dataType, rawValue, formula, arrayRef, styleIndex };
1856
1925
  }
1857
1926
  };
1858
1927
  _data = new WeakMap();
@@ -2075,6 +2144,14 @@ var Worksheet = class {
2075
2144
  getCellValue(reference) {
2076
2145
  return this.tryGetCell(reference)?.getValue() ?? null;
2077
2146
  }
2147
+ /** Formula text (without leading '=') of a cell, or null if it has none. */
2148
+ getCellFormula(reference) {
2149
+ return this.tryGetCell(reference)?.getFormula() ?? null;
2150
+ }
2151
+ /** Spill range (e.g. "A1:A3") of a dynamic-array anchor, or null. Supports A1#. */
2152
+ getSpillRange(reference) {
2153
+ return this.tryGetCell(reference)?.getArrayRef() ?? null;
2154
+ }
2078
2155
  /**
2079
2156
  * Returns the 1-based extent of populated cells (largest row and column that
2080
2157
  * contain data). Returns zeros for an empty sheet. Useful for snapshotting a
@@ -2125,6 +2202,15 @@ var Worksheet = class {
2125
2202
  row.height = height;
2126
2203
  row.customHeight = true;
2127
2204
  }
2205
+ /** Shows or hides a row (1-based index). Hidden rows affect SUBTOTAL(101-111). */
2206
+ setRowHidden(rowIndex, hidden = true) {
2207
+ if (rowIndex < 1) throw new RangeError("Row index must be >= 1.");
2208
+ __privateMethod(this, _Worksheet_instances, getOrCreateRow_fn).call(this, rowIndex).hidden = hidden;
2209
+ }
2210
+ /** True if the given 1-based row is hidden. */
2211
+ isRowHidden(rowIndex) {
2212
+ return __privateGet(this, _rows).get(rowIndex)?.hidden === true;
2213
+ }
2128
2214
  /** Merges cells in the given range (e.g. "A1:B2"). */
2129
2215
  mergeCells(rangeReference) {
2130
2216
  if (!rangeReference) throw new Error("Range reference cannot be empty.");
@@ -2224,6 +2310,7 @@ ${colsXml}${sheetDataXml}${filterXml}${mergeXml}${validXml}${drawingXml}
2224
2310
  rowData.height = parseFloat(ht);
2225
2311
  rowData.customHeight = true;
2226
2312
  }
2313
+ if (getAttr(rowEl, "hidden") === "1") rowData.hidden = true;
2227
2314
  for (const cellEl of rowEl.children) {
2228
2315
  const tag = cellEl.tagName.replace(/^.*:/, "");
2229
2316
  if (tag !== "c") continue;
@@ -2289,14 +2376,15 @@ buildSheetDataXml_fn = function() {
2289
2376
  if (sortedRows.length === 0) return "<sheetData/>";
2290
2377
  const rowsXml = sortedRows.map((row) => {
2291
2378
  const ht = row.height !== void 0 && row.customHeight ? ` ht="${row.height}" customHeight="1"` : "";
2379
+ const hidden = row.hidden ? ' hidden="1"' : "";
2292
2380
  const sortedCells = [...row.cells.values()].sort((a, b) => {
2293
2381
  const ra = parseCellRef(a.reference);
2294
2382
  const rb = parseCellRef(b.reference);
2295
2383
  return ra.column - rb.column;
2296
2384
  });
2297
2385
  const cellsXml = sortedCells.map((cd) => new Cell(cd, __privateGet(this, _sharedStrings2), __privateGet(this, _styles2)).toXml()).filter(Boolean).join("");
2298
- if (!cellsXml && !ht) return "";
2299
- return `<row r="${row.rowIndex}"${ht}>${cellsXml}</row>`;
2386
+ if (!cellsXml && !ht && !hidden) return "";
2387
+ return `<row r="${row.rowIndex}"${ht}${hidden}>${cellsXml}</row>`;
2300
2388
  }).filter(Boolean).join("");
2301
2389
  return `<sheetData>${rowsXml}</sheetData>`;
2302
2390
  };
@@ -2335,14 +2423,77 @@ function escXml2(s) {
2335
2423
  return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2336
2424
  }
2337
2425
 
2426
+ // src/model/defined-name.ts
2427
+ var _names, _DefinedNameManager_instances, indexOf_fn;
2428
+ var DefinedNameManager = class {
2429
+ constructor() {
2430
+ __privateAdd(this, _DefinedNameManager_instances);
2431
+ __privateAdd(this, _names, []);
2432
+ }
2433
+ /** All defined names, in insertion order. */
2434
+ get all() {
2435
+ return __privateGet(this, _names);
2436
+ }
2437
+ get size() {
2438
+ return __privateGet(this, _names).length;
2439
+ }
2440
+ /**
2441
+ * Adds or replaces a defined name. A name is uniquely identified by the pair
2442
+ * (identifier, scope): the same identifier may exist once globally and once
2443
+ * per worksheet. The leading '=' of refersTo is stripped.
2444
+ */
2445
+ define(name, refersTo, scope) {
2446
+ if (!name) throw new Error("Defined name cannot be empty.");
2447
+ if (!refersTo) throw new Error("Defined name refersTo cannot be empty.");
2448
+ const cleaned = refersTo.startsWith("=") ? refersTo.slice(1) : refersTo;
2449
+ const idx = __privateMethod(this, _DefinedNameManager_instances, indexOf_fn).call(this, name, scope);
2450
+ const entry = { name, refersTo: cleaned, scope };
2451
+ if (idx >= 0) __privateGet(this, _names)[idx] = entry;
2452
+ else __privateGet(this, _names).push(entry);
2453
+ }
2454
+ /** Removes a defined name by identifier and scope. Returns true if removed. */
2455
+ remove(name, scope) {
2456
+ const idx = __privateMethod(this, _DefinedNameManager_instances, indexOf_fn).call(this, name, scope);
2457
+ if (idx < 0) return false;
2458
+ __privateGet(this, _names).splice(idx, 1);
2459
+ return true;
2460
+ }
2461
+ /**
2462
+ * Resolves a name to its refers-to expression following Excel scoping:
2463
+ * a name scoped to `sheetName` takes precedence over the workbook-global
2464
+ * name of the same identifier. Returns undefined if no name matches.
2465
+ */
2466
+ resolve(name, sheetName) {
2467
+ const key = name.toUpperCase();
2468
+ if (sheetName) {
2469
+ const local = __privateGet(this, _names).find(
2470
+ (n) => n.name.toUpperCase() === key && n.scope?.toUpperCase() === sheetName.toUpperCase()
2471
+ );
2472
+ if (local) return local.refersTo;
2473
+ }
2474
+ const global = __privateGet(this, _names).find((n) => n.name.toUpperCase() === key && n.scope === void 0);
2475
+ return global?.refersTo;
2476
+ }
2477
+ };
2478
+ _names = new WeakMap();
2479
+ _DefinedNameManager_instances = new WeakSet();
2480
+ indexOf_fn = function(name, scope) {
2481
+ const key = name.toUpperCase();
2482
+ const scopeKey = scope?.toUpperCase();
2483
+ return __privateGet(this, _names).findIndex(
2484
+ (n) => n.name.toUpperCase() === key && n.scope?.toUpperCase() === scopeKey
2485
+ );
2486
+ };
2487
+
2338
2488
  // src/model/workbook.ts
2339
- var _sharedStrings3, _styles3, _sheets, _closed, _Workbook_instances, assertOpen_fn, buildZipEntries_fn;
2489
+ var _sharedStrings3, _styles3, _sheets, _definedNames, _closed, _Workbook_instances, assertOpen_fn, buildZipEntries_fn;
2340
2490
  var _Workbook = class _Workbook {
2341
2491
  constructor(sharedStrings, styles) {
2342
2492
  __privateAdd(this, _Workbook_instances);
2343
2493
  __privateAdd(this, _sharedStrings3);
2344
2494
  __privateAdd(this, _styles3);
2345
2495
  __privateAdd(this, _sheets, []);
2496
+ __privateAdd(this, _definedNames, new DefinedNameManager());
2346
2497
  __privateAdd(this, _closed, false);
2347
2498
  __privateSet(this, _sharedStrings3, sharedStrings);
2348
2499
  __privateSet(this, _styles3, styles);
@@ -2386,6 +2537,19 @@ var _Workbook = class _Workbook {
2386
2537
  ws.loadFromXml(wsXml);
2387
2538
  __privateGet(wb, _sheets).push({ worksheet: ws, sheetId, relId });
2388
2539
  }
2540
+ for (const dn of getElementsByTagName(wbRoot, "definedName")) {
2541
+ const name = getAttr(dn, "name");
2542
+ if (!name) continue;
2543
+ const refersTo = dn.textContent.trim();
2544
+ if (!refersTo) continue;
2545
+ const localSheetId = getAttr(dn, "localSheetId");
2546
+ let scope;
2547
+ if (localSheetId !== void 0) {
2548
+ const idx = parseInt(localSheetId, 10);
2549
+ scope = __privateGet(wb, _sheets)[idx]?.worksheet.name;
2550
+ }
2551
+ __privateGet(wb, _definedNames).define(name, refersTo, scope);
2552
+ }
2389
2553
  return wb;
2390
2554
  }
2391
2555
  /**
@@ -2430,6 +2594,43 @@ var _Workbook = class _Workbook {
2430
2594
  if (!entry) throw new Error(`Worksheet "${name}" not found.`);
2431
2595
  return entry.worksheet;
2432
2596
  }
2597
+ // ── Defined names (named ranges) ─────────────────────────────────────────────
2598
+ /**
2599
+ * Defines (or replaces) a named range.
2600
+ *
2601
+ * @param name The name identifier (case-insensitive when resolved).
2602
+ * @param refersTo The refers-to expression, e.g. "Sheet1!$A$1:$B$10". A
2603
+ * leading '=' is optional and stripped.
2604
+ * @param options `scope` restricts the name to a single worksheet; omit for
2605
+ * a workbook-global name.
2606
+ */
2607
+ defineName(name, refersTo, options) {
2608
+ __privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
2609
+ const scope = options?.scope;
2610
+ if (scope !== void 0 && !__privateGet(this, _sheets).some((e) => e.worksheet.name === scope)) {
2611
+ throw new Error(`Cannot scope defined name "${name}" to unknown worksheet "${scope}".`);
2612
+ }
2613
+ __privateGet(this, _definedNames).define(name, refersTo, scope);
2614
+ }
2615
+ /**
2616
+ * Resolves a defined name to its refers-to expression (without leading '=').
2617
+ * Follows Excel scoping: a name scoped to `sheetName` wins over the global
2618
+ * name of the same identifier. Returns undefined if no name matches.
2619
+ *
2620
+ * This is the seam the formula engine calls to resolve NamedRangeNode.
2621
+ */
2622
+ getDefinedName(name, sheetName) {
2623
+ return __privateGet(this, _definedNames).resolve(name, sheetName);
2624
+ }
2625
+ /** Removes a defined name. Returns true if a matching name was removed. */
2626
+ removeName(name, scope) {
2627
+ __privateMethod(this, _Workbook_instances, assertOpen_fn).call(this);
2628
+ return __privateGet(this, _definedNames).remove(name, scope);
2629
+ }
2630
+ /** All defined names in the workbook, in insertion order. */
2631
+ get definedNames() {
2632
+ return __privateGet(this, _definedNames).all;
2633
+ }
2433
2634
  // ── Serialisation ──────────────────────────────────────────────────────────
2434
2635
  /**
2435
2636
  * Serialises the workbook to a Uint8Array (xlsx binary).
@@ -2467,6 +2668,7 @@ var _Workbook = class _Workbook {
2467
2668
  _sharedStrings3 = new WeakMap();
2468
2669
  _styles3 = new WeakMap();
2469
2670
  _sheets = new WeakMap();
2671
+ _definedNames = new WeakMap();
2470
2672
  _closed = new WeakMap();
2471
2673
  _Workbook_instances = new WeakSet();
2472
2674
  // ── Private helpers ────────────────────────────────────────────────────────
@@ -2483,7 +2685,15 @@ buildZipEntries_fn = function() {
2483
2685
  relationshipId: e.relId,
2484
2686
  state: e.worksheet.visibility !== "visible" ? e.worksheet.visibility : void 0
2485
2687
  }));
2486
- entries.set("xl/workbook.xml", buildWorkbookXml(sheetMetas));
2688
+ const definedNameMetas = __privateGet(this, _definedNames).all.map((dn) => {
2689
+ let localSheetId;
2690
+ if (dn.scope !== void 0) {
2691
+ const idx = __privateGet(this, _sheets).findIndex((e) => e.worksheet.name === dn.scope);
2692
+ if (idx >= 0) localSheetId = idx;
2693
+ }
2694
+ return { name: dn.name, refersTo: dn.refersTo, localSheetId };
2695
+ });
2696
+ entries.set("xl/workbook.xml", buildWorkbookXml(sheetMetas, definedNameMetas));
2487
2697
  entries.set("xl/_rels/workbook.xml.rels", buildWorkbookRels(sheetCount));
2488
2698
  entries.set("xl/styles.xml", __privateGet(this, _styles3).buildStylesXml());
2489
2699
  entries.set("xl/sharedStrings.xml", __privateGet(this, _sharedStrings3).buildXml());
@@ -2544,6 +2754,7 @@ function resolveWorksheetPath(relsXml, relId) {
2544
2754
  exports.CONTENT_TYPES = CONTENT_TYPES;
2545
2755
  exports.Cell = Cell;
2546
2756
  exports.CellStyle = CellStyle;
2757
+ exports.DefinedNameManager = DefinedNameManager;
2547
2758
  exports.ExcelAlignment = ExcelAlignment;
2548
2759
  exports.ExcelBorder = ExcelBorder;
2549
2760
  exports.ExcelBorderEdge = ExcelBorderEdge;