@shbernal/ts-xlsx 3.0.0 → 3.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.
Files changed (277) hide show
  1. package/README.md +34 -24
  2. package/dist/bytes.d.ts +10 -2
  3. package/dist/bytes.js +22 -0
  4. package/dist/core/address.d.ts +96 -2
  5. package/dist/core/address.js +87 -33
  6. package/dist/core/array-formula-ranges.d.ts +33 -0
  7. package/dist/core/array-formula-ranges.js +62 -0
  8. package/dist/core/autofilter.d.ts +4 -3
  9. package/dist/core/autofilter.js +31 -30
  10. package/dist/core/axis-handle.d.ts +2 -0
  11. package/dist/core/axis-handle.js +5 -2
  12. package/dist/core/cell.d.ts +4 -3
  13. package/dist/core/cell.js +4 -6
  14. package/dist/core/clone.d.ts +29 -0
  15. package/dist/core/clone.js +24 -0
  16. package/dist/core/color-resolution.js +4 -6
  17. package/dist/core/column.d.ts +1 -0
  18. package/dist/core/column.js +6 -2
  19. package/dist/core/comment-thread.d.ts +7 -3
  20. package/dist/core/comment-thread.js +2 -1
  21. package/dist/core/conditional-formatting-overlay.d.ts +11 -1
  22. package/dist/core/conditional-formatting-overlay.js +30 -3
  23. package/dist/core/conditional-formatting.d.ts +107 -11
  24. package/dist/core/conditional-formatting.js +76 -19
  25. package/dist/core/containers.d.ts +12 -0
  26. package/dist/core/containers.js +5 -0
  27. package/dist/core/data-validation-overlay.d.ts +7 -1
  28. package/dist/core/data-validation-overlay.js +19 -4
  29. package/dist/core/data-validation.d.ts +22 -0
  30. package/dist/core/data-validation.js +30 -4
  31. package/dist/core/date-format.d.ts +20 -0
  32. package/dist/core/date-format.js +170 -0
  33. package/dist/core/date.d.ts +43 -8
  34. package/dist/core/date.js +19 -3
  35. package/dist/core/formula-references.d.ts +26 -0
  36. package/dist/core/formula-references.js +173 -0
  37. package/dist/core/formula-scan.d.ts +2 -0
  38. package/dist/core/formula-scan.js +58 -0
  39. package/dist/core/formula.d.ts +76 -17
  40. package/dist/core/formula.js +87 -80
  41. package/dist/core/function-values.d.ts +4 -0
  42. package/dist/core/function-values.js +58 -0
  43. package/dist/core/future-functions.d.ts +3 -0
  44. package/dist/core/{modern-functions.js → future-functions.js} +119 -88
  45. package/dist/core/grid-edits.d.ts +30 -3
  46. package/dist/core/grid-edits.js +248 -81
  47. package/dist/core/grid-shift.d.ts +58 -9
  48. package/dist/core/grid-shift.js +26 -4
  49. package/dist/core/hyperlink.d.ts +39 -0
  50. package/dist/core/hyperlink.js +88 -0
  51. package/dist/core/image.d.ts +49 -14
  52. package/dist/core/image.js +16 -5
  53. package/dist/core/internal.d.ts +10 -2
  54. package/dist/core/limits.d.ts +25 -0
  55. package/dist/core/limits.js +4 -0
  56. package/dist/core/merge.d.ts +18 -4
  57. package/dist/core/merge.js +33 -20
  58. package/dist/core/page-setup.d.ts +26 -11
  59. package/dist/core/pivot-table.d.ts +49 -10
  60. package/dist/core/pivot-table.js +112 -60
  61. package/dist/core/protection.d.ts +66 -4
  62. package/dist/core/protection.js +6 -11
  63. package/dist/core/range.js +34 -30
  64. package/dist/core/row-input.js +2 -2
  65. package/dist/core/row.d.ts +1 -0
  66. package/dist/core/row.js +6 -2
  67. package/dist/core/structured-reference.d.ts +7 -0
  68. package/dist/core/structured-reference.js +7 -0
  69. package/dist/core/style.d.ts +85 -70
  70. package/dist/core/style.js +92 -28
  71. package/dist/core/table-style.d.ts +1 -1
  72. package/dist/core/table-style.js +4 -4
  73. package/dist/core/table.d.ts +64 -16
  74. package/dist/core/table.js +87 -40
  75. package/dist/core/theme.d.ts +2 -0
  76. package/dist/core/theme.js +9 -5
  77. package/dist/core/value.d.ts +60 -28
  78. package/dist/core/value.js +48 -43
  79. package/dist/core/workbook-media.d.ts +34 -0
  80. package/dist/core/workbook-media.js +44 -0
  81. package/dist/core/workbook-styles.d.ts +83 -1
  82. package/dist/core/workbook-styles.js +4 -0
  83. package/dist/core/workbook-theme.js +5 -3
  84. package/dist/core/workbook-vba.js +3 -2
  85. package/dist/core/workbook.d.ts +41 -3
  86. package/dist/core/workbook.js +46 -41
  87. package/dist/core/worksheet-comments.d.ts +2 -1
  88. package/dist/core/worksheet-comments.js +20 -13
  89. package/dist/core/worksheet-merges.d.ts +49 -0
  90. package/dist/core/worksheet-merges.js +65 -0
  91. package/dist/core/worksheet-model.d.ts +10 -2
  92. package/dist/core/worksheet-model.js +28 -9
  93. package/dist/core/worksheet-pictures.d.ts +3 -3
  94. package/dist/core/worksheet-pictures.js +7 -6
  95. package/dist/core/worksheet.d.ts +165 -27
  96. package/dist/core/worksheet.js +206 -89
  97. package/dist/customui/ribbon.js +2 -1
  98. package/dist/entries/core.d.ts +21 -14
  99. package/dist/entries/core.js +14 -7
  100. package/dist/entries/errors.d.ts +1 -0
  101. package/dist/entries/errors.js +1 -0
  102. package/dist/entries/xlsx.d.ts +1 -1
  103. package/dist/errors.d.ts +64 -0
  104. package/dist/errors.js +22 -0
  105. package/dist/hex.d.ts +8 -0
  106. package/dist/hex.js +3 -0
  107. package/dist/io/cell-metadata/metadata.d.ts +42 -0
  108. package/dist/io/cell-metadata/metadata.js +35 -0
  109. package/dist/io/cell-metadata/rich-values.d.ts +11 -0
  110. package/dist/io/cell-metadata/rich-values.js +80 -0
  111. package/dist/io/csv/delimiter.d.ts +21 -0
  112. package/dist/io/csv/delimiter.js +18 -0
  113. package/dist/io/csv/errors.d.ts +14 -0
  114. package/dist/io/csv/errors.js +5 -0
  115. package/dist/io/csv/read.d.ts +6 -1
  116. package/dist/io/csv/read.js +51 -22
  117. package/dist/io/csv/write.d.ts +19 -5
  118. package/dist/io/csv/write.js +24 -44
  119. package/dist/io/opc/inflate.js +14 -0
  120. package/dist/io/opc/namespaces.d.ts +16 -0
  121. package/dist/io/opc/namespaces.js +4 -0
  122. package/dist/io/opc/part-paths.d.ts +9 -0
  123. package/dist/io/opc/part-paths.js +5 -5
  124. package/dist/io/opc/read-opc.d.ts +58 -14
  125. package/dist/io/opc/read-opc.js +49 -21
  126. package/dist/io/opc/read-options.d.ts +5 -1
  127. package/dist/io/opc/sniff-format.d.ts +3 -3
  128. package/dist/io/opc/sniff-format.js +2 -2
  129. package/dist/io/opc/strict-relationships.d.ts +2 -0
  130. package/dist/io/opc/strict-relationships.js +12 -0
  131. package/dist/io/read-policy/column-budget.d.ts +26 -0
  132. package/dist/io/read-policy/column-budget.js +18 -0
  133. package/dist/io/read-policy/read-repair.d.ts +74 -0
  134. package/dist/io/read-policy/read-repair.js +119 -0
  135. package/dist/io/style/cell-style-resolution.d.ts +25 -0
  136. package/dist/io/style/cell-style-resolution.js +30 -0
  137. package/dist/io/style/xf-style.d.ts +23 -1
  138. package/dist/io/style/xf-style.js +8 -0
  139. package/dist/io/xlsb/formula.d.ts +2 -2
  140. package/dist/io/xlsb/formula.js +52 -35
  141. package/dist/io/xlsb/primitives.js +8 -14
  142. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  143. package/dist/io/xlsb/ptg-functions.js +495 -596
  144. package/dist/io/xlsb/read-metadata.d.ts +7 -0
  145. package/dist/io/xlsb/read-metadata.js +69 -0
  146. package/dist/io/xlsb/read-styles.js +45 -81
  147. package/dist/io/xlsb/read-worksheet.d.ts +26 -1
  148. package/dist/io/xlsb/read-worksheet.js +148 -106
  149. package/dist/io/xlsb/read.d.ts +6 -3
  150. package/dist/io/xlsb/read.js +70 -31
  151. package/dist/io/xlsb/record-stream.d.ts +30 -0
  152. package/dist/io/xlsb/record-stream.js +20 -0
  153. package/dist/io/xlsb/record-types.d.ts +14 -5
  154. package/dist/io/xlsb/record-types.js +14 -5
  155. package/dist/io/xlsb/sheet-protection.d.ts +20 -0
  156. package/dist/io/xlsb/sheet-protection.js +60 -0
  157. package/dist/io/xlsx/cell-accumulator.d.ts +24 -9
  158. package/dist/io/xlsx/cell-accumulator.js +74 -21
  159. package/dist/io/xlsx/cell-metadata.d.ts +39 -0
  160. package/dist/io/xlsx/cell-metadata.js +162 -0
  161. package/dist/io/xlsx/cell-value.d.ts +19 -5
  162. package/dist/io/xlsx/cell-value.js +45 -30
  163. package/dist/io/xlsx/color-xml.js +3 -2
  164. package/dist/io/xlsx/column-span.d.ts +11 -0
  165. package/dist/io/xlsx/column-span.js +9 -0
  166. package/dist/io/xlsx/comments.d.ts +9 -30
  167. package/dist/io/xlsx/comments.js +25 -83
  168. package/dist/io/xlsx/conditional-formatting.d.ts +43 -23
  169. package/dist/io/xlsx/conditional-formatting.js +437 -138
  170. package/dist/io/xlsx/data-validation.d.ts +12 -7
  171. package/dist/io/xlsx/data-validation.js +67 -53
  172. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  173. package/dist/io/xlsx/edit-vba.js +52 -52
  174. package/dist/io/xlsx/font-xml.d.ts +7 -0
  175. package/dist/io/xlsx/font-xml.js +104 -0
  176. package/dist/io/xlsx/hyperlinks.d.ts +19 -21
  177. package/dist/io/xlsx/hyperlinks.js +12 -32
  178. package/dist/io/xlsx/images.d.ts +49 -16
  179. package/dist/io/xlsx/images.js +149 -50
  180. package/dist/io/xlsx/namespaces.d.ts +24 -0
  181. package/dist/io/xlsx/namespaces.js +11 -0
  182. package/dist/io/xlsx/package-plan.d.ts +60 -15
  183. package/dist/io/xlsx/package-plan.js +83 -18
  184. package/dist/io/xlsx/part-names.d.ts +6 -8
  185. package/dist/io/xlsx/part-names.js +3 -7
  186. package/dist/io/xlsx/pivot.d.ts +5 -5
  187. package/dist/io/xlsx/pivot.js +26 -24
  188. package/dist/io/xlsx/preserved-splices.d.ts +6 -0
  189. package/dist/io/xlsx/preserved-splices.js +74 -0
  190. package/dist/io/xlsx/read-comments.d.ts +29 -0
  191. package/dist/io/xlsx/read-comments.js +75 -0
  192. package/dist/io/xlsx/read-parts.d.ts +59 -0
  193. package/dist/io/xlsx/read-parts.js +259 -0
  194. package/dist/io/xlsx/read-pivot.js +11 -4
  195. package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +8 -5
  196. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +24 -4
  197. package/dist/io/xlsx/read-rows.d.ts +35 -11
  198. package/dist/io/xlsx/read-rows.js +73 -49
  199. package/dist/io/xlsx/read-shared-strings.js +3 -2
  200. package/dist/io/xlsx/read-styles.d.ts +0 -30
  201. package/dist/io/xlsx/read-styles.js +45 -105
  202. package/dist/io/xlsx/read-workbook-xml.d.ts +42 -0
  203. package/dist/io/xlsx/read-workbook-xml.js +175 -0
  204. package/dist/io/xlsx/read-worksheet.d.ts +9 -3
  205. package/dist/io/xlsx/read-worksheet.js +70 -177
  206. package/dist/io/xlsx/read.d.ts +3 -14
  207. package/dist/io/xlsx/read.js +74 -411
  208. package/dist/io/xlsx/relationships.d.ts +3 -0
  209. package/dist/io/xlsx/relationships.js +4 -0
  210. package/dist/io/xlsx/rich-text.js +1 -1
  211. package/dist/io/xlsx/row-position.d.ts +20 -0
  212. package/dist/io/xlsx/row-position.js +11 -0
  213. package/dist/io/xlsx/row-xml.d.ts +139 -0
  214. package/dist/io/xlsx/row-xml.js +222 -0
  215. package/dist/io/xlsx/shared-formulas.js +2 -2
  216. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  217. package/dist/io/xlsx/sheet-properties.js +182 -20
  218. package/dist/io/xlsx/strict-parts.d.ts +7 -0
  219. package/dist/io/xlsx/strict-parts.js +171 -0
  220. package/dist/io/xlsx/style-elements.d.ts +41 -0
  221. package/dist/io/xlsx/style-elements.js +136 -0
  222. package/dist/io/xlsx/styles.d.ts +17 -3
  223. package/dist/io/xlsx/styles.js +13 -209
  224. package/dist/io/xlsx/tables.d.ts +10 -2
  225. package/dist/io/xlsx/tables.js +55 -52
  226. package/dist/io/xlsx/theme-xml.d.ts +32 -4
  227. package/dist/io/xlsx/theme-xml.js +106 -71
  228. package/dist/io/xlsx/threaded-comments.d.ts +14 -11
  229. package/dist/io/xlsx/threaded-comments.js +16 -8
  230. package/dist/io/xlsx/workbook-xml.d.ts +45 -6
  231. package/dist/io/xlsx/workbook-xml.js +62 -54
  232. package/dist/io/xlsx/worksheet-xml.d.ts +26 -70
  233. package/dist/io/xlsx/worksheet-xml.js +69 -282
  234. package/dist/io/xlsx/write-stream.d.ts +90 -19
  235. package/dist/io/xlsx/write-stream.js +80 -25
  236. package/dist/io/xlsx/write.d.ts +12 -6
  237. package/dist/io/xlsx/write.js +226 -123
  238. package/dist/rel-type.d.ts +25 -0
  239. package/dist/rel-type.js +9 -0
  240. package/dist/token-set.d.ts +13 -0
  241. package/dist/token-set.js +4 -1
  242. package/dist/vba/bytes.d.ts +16 -5
  243. package/dist/vba/bytes.js +27 -6
  244. package/dist/vba/cfb-format.d.ts +55 -0
  245. package/dist/vba/cfb-format.js +35 -0
  246. package/dist/vba/cfb-writer.d.ts +8 -0
  247. package/dist/vba/cfb-writer.js +40 -40
  248. package/dist/vba/cfb.d.ts +21 -4
  249. package/dist/vba/cfb.js +124 -90
  250. package/dist/vba/codepage.js +2 -1
  251. package/dist/vba/dir-records.d.ts +6 -0
  252. package/dist/vba/dir-records.js +10 -2
  253. package/dist/vba/errors.d.ts +11 -4
  254. package/dist/vba/index.d.ts +0 -2
  255. package/dist/vba/index.js +0 -2
  256. package/dist/vba/ms-ovba.d.ts +6 -2
  257. package/dist/vba/ms-ovba.js +66 -22
  258. package/dist/vba/project-editor.js +129 -119
  259. package/dist/vba/project.d.ts +15 -1
  260. package/dist/vba/project.js +38 -13
  261. package/dist/vba/vba-encoding.d.ts +4 -10
  262. package/dist/vba/vba-encoding.js +3 -17
  263. package/dist/xml/xml-attrs.d.ts +37 -0
  264. package/dist/xml/xml-attrs.js +30 -0
  265. package/dist/xml/xml-chars.d.ts +40 -0
  266. package/dist/xml/xml-chars.js +12 -0
  267. package/dist/xml/xml-namespaces.d.ts +31 -0
  268. package/dist/xml/xml-namespaces.js +50 -0
  269. package/dist/xml/xml-read.d.ts +101 -11
  270. package/dist/xml/xml-read.js +150 -18
  271. package/dist/xml/xml-scan.d.ts +25 -34
  272. package/dist/xml/xml-scan.js +69 -46
  273. package/dist/xml/xml.d.ts +59 -26
  274. package/dist/xml/xml.js +45 -23
  275. package/package.json +15 -9
  276. package/skills/ts-xlsx-upstream/SKILL.md +21 -13
  277. package/dist/core/modern-functions.d.ts +0 -1
@@ -3,20 +3,25 @@ import type { Worksheet } from '../../core/worksheet.ts';
3
3
  import { type CollectingPass } from '../../xml/xml-read.ts';
4
4
  /** The standard `<dataValidations>` element for the rules stored in the legacy form, or '' when the
5
5
  * sheet has none of them, so a sheet with only extended (or no) validations stays byte-clean here.
6
- * The extended rules are emitted separately by {@link dataValidationsExtXml}. */
7
- export declare function dataValidationsXml(entries: readonly DataValidationEntry[]): string;
6
+ * The extended rules are emitted separately by {@link dataValidationsExtXml}.
7
+ *
8
+ * An operand is stored with the function prefixes a cell formula takes, as Excel stores it;
9
+ * `formulaNames` is `formulaNamesInScope` for the sheet. */
10
+ export declare function dataValidationsXml(entries: readonly DataValidationEntry[], formulaNames: ReadonlySet<string>): string;
8
11
  /** The `<ext>` carrying the extended (`<x14:dataValidation>`) rules, or '' when the sheet declares
9
12
  * none. Emitted bare (no `<extLst>` wrapper) so the worksheet serialiser can gather it into a single
10
13
  * `<extLst>` beside the conditional-formatting extension: a worksheet may carry at most one. */
11
- export declare function dataValidationsExtXml(entries: readonly DataValidationEntry[]): string;
14
+ export declare function dataValidationsExtXml(entries: readonly DataValidationEntry[], formulaNames: ReadonlySet<string>): string;
12
15
  /** A pass gathering the standard `<dataValidation>` elements of a worksheet part, for a caller
13
- * reading the part alongside its other readers in one parse. */
14
- export declare function dataValidationPass(): CollectingPass<DataValidationEntry[]>;
16
+ * reading the part alongside its other readers in one parse. An operand sheds its function prefixes
17
+ * against `definedNames`, the workbook's names as `definedNameKeys` spells them. */
18
+ export declare function dataValidationPass(definedNames: ReadonlySet<string>): CollectingPass<DataValidationEntry[]>;
15
19
  /** A pass gathering the extended `<x14:dataValidation>` elements of a worksheet's `<extLst>`, so a
16
20
  * cross-sheet or whole-column list validation Excel stored only in the 2009 extension form is read
17
21
  * back rather than dropped. The standard pass ignores these (they are prefixed); this one,
18
- * symmetrically, handles only the prefixed elements. */
19
- export declare function extendedDataValidationPass(): CollectingPass<DataValidationEntry[]>;
22
+ * symmetrically, handles only the prefixed elements. An operand sheds its function prefixes against
23
+ * `definedNames`, as the standard pass's does. */
24
+ export declare function extendedDataValidationPass(definedNames: ReadonlySet<string>): CollectingPass<DataValidationEntry[]>;
20
25
  /** Fold parsed validations onto a sheet, each bound to its original range and carrying its form: an
21
26
  * `extended` entry is re-attached as extended so a round-trip writes it back to the x14 block. */
22
27
  export declare function applyDataValidations(sheet: Worksheet, entries: readonly DataValidationEntry[]): void;
@@ -1,23 +1,30 @@
1
- import { isDataValidationErrorStyle, isDataValidationOperator, isDataValidationType, } from '../../core/data-validation.js';
1
+ import { isDataValidationErrorStyle, isDataValidationImeMode, isDataValidationOperator, isDataValidationType, } from '../../core/data-validation.js';
2
+ import { mangleFormula, stripFormulaEquals, unmangleFunctions } from '../../core/formula.js';
2
3
  import { decodeSqrefRects } from '../../core/merge.js';
4
+ import { coerceNumericLiteral, enumToken } from '../../xml/xml-attrs.js';
3
5
  import { TextCapture } from '../../xml/xml-read.js';
4
- import { boolStrict, coerceNumericLiteral, localName } from '../../xml/xml-scan.js';
5
- import { checkedToken, escapeAttr, escapeText, stripFormulaEquals, textAttr } from '../../xml/xml.js';
6
- import { DATA_VALIDATION_EXT_URI, XM_NS } from './namespaces.js';
6
+ import { boolStrict, localName } from '../../xml/xml-scan.js';
7
+ import { checkedToken, escapeAttr, escapeText, textAttr } from '../../xml/xml.js';
8
+ import { admitting } from '../read-policy/read-repair.js';
9
+ import { DATA_VALIDATION_EXT_URI, isExtensionElement, isMainNamespaceElement, XM_NS, } from './namespaces.js';
7
10
  import { x14Ext } from './x14-ext.js';
8
11
  const TYPED = new Set(['whole', 'decimal', 'date', 'time', 'textLength']);
9
- export function dataValidationsXml(entries) {
12
+ export function dataValidationsXml(entries, formulaNames) {
10
13
  const standard = entries.filter((entry) => !entry.extended);
11
14
  if (standard.length === 0)
12
15
  return '';
13
- const items = standard.map(({ sqref, rule }) => dataValidationXml(sqref, rule)).join('');
16
+ const items = standard
17
+ .map(({ sqref, rule }) => dataValidationXml(sqref, rule, formulaNames))
18
+ .join('');
14
19
  return `<dataValidations count="${standard.length}">${items}</dataValidations>`;
15
20
  }
16
- export function dataValidationsExtXml(entries) {
21
+ export function dataValidationsExtXml(entries, formulaNames) {
17
22
  const extended = entries.filter((entry) => entry.extended);
18
23
  if (extended.length === 0)
19
24
  return '';
20
- const items = extended.map(({ sqref, rule }) => extendedDataValidationXml(sqref, rule)).join('');
25
+ const items = extended
26
+ .map(({ sqref, rule }) => extendedDataValidationXml(sqref, rule, formulaNames))
27
+ .join('');
21
28
  return x14Ext(DATA_VALIDATION_EXT_URI, `<x14:dataValidations count="${extended.length}" xmlns:xm="${XM_NS}">${items}</x14:dataValidations>`);
22
29
  }
23
30
  function ruleAttrs(rule) {
@@ -25,10 +32,14 @@ function ruleAttrs(rule) {
25
32
  (rule.errorStyle === undefined
26
33
  ? ''
27
34
  : ` errorStyle="${checkedToken(rule.errorStyle, isDataValidationErrorStyle, 'data validation error style')}"`) +
35
+ (rule.imeMode === undefined
36
+ ? ''
37
+ : ` imeMode="${checkedToken(rule.imeMode, isDataValidationImeMode, 'data validation IME mode')}"`) +
28
38
  (rule.operator === undefined
29
39
  ? ''
30
40
  : ` operator="${checkedToken(rule.operator, isDataValidationOperator, 'data validation operator')}"`) +
31
41
  (rule.allowBlank ? ' allowBlank="1"' : '') +
42
+ (rule.suppressDropDown ? ' showDropDown="1"' : '') +
32
43
  (rule.showInputMessage ? ' showInputMessage="1"' : '') +
33
44
  (rule.showErrorMessage ? ' showErrorMessage="1"' : '') +
34
45
  textAttr('errorTitle', rule.errorTitle) +
@@ -36,60 +47,54 @@ function ruleAttrs(rule) {
36
47
  textAttr('promptTitle', rule.promptTitle) +
37
48
  textAttr('prompt', rule.prompt));
38
49
  }
39
- function dataValidationXml(sqref, rule) {
40
- const [f1, f2] = operands(rule);
41
- const body = (f1 !== undefined ? `<formula1>${escapeText(stripFormulaEquals(f1))}</formula1>` : '') +
42
- (f2 !== undefined ? `<formula2>${escapeText(stripFormulaEquals(f2))}</formula2>` : '');
50
+ function dataValidationXml(sqref, rule, formulaNames) {
51
+ const [f1, f2] = rule.formulae ?? [];
52
+ const body = (f1 !== undefined ? `<formula1>${operandText(f1, formulaNames)}</formula1>` : '') +
53
+ (f2 !== undefined ? `<formula2>${operandText(f2, formulaNames)}</formula2>` : '');
43
54
  return `<dataValidation${ruleAttrs(rule)} sqref="${escapeAttr(sqref)}">${body}</dataValidation>`;
44
55
  }
45
- function operands(rule) {
46
- const drop = (v) => typeof v === 'number' && !Number.isFinite(v) ? undefined : v;
56
+ function extendedDataValidationXml(sqref, rule, formulaNames) {
47
57
  const [f1, f2] = rule.formulae ?? [];
48
- return [drop(f1), drop(f2)];
49
- }
50
- function extendedDataValidationXml(sqref, rule) {
51
- const [f1, f2] = operands(rule);
52
58
  const body = (f1 !== undefined
53
- ? `<x14:formula1><xm:f>${escapeText(stripFormulaEquals(f1))}</xm:f></x14:formula1>`
59
+ ? `<x14:formula1><xm:f>${operandText(f1, formulaNames)}</xm:f></x14:formula1>`
54
60
  : '') +
55
61
  (f2 !== undefined
56
- ? `<x14:formula2><xm:f>${escapeText(stripFormulaEquals(f2))}</xm:f></x14:formula2>`
62
+ ? `<x14:formula2><xm:f>${operandText(f2, formulaNames)}</xm:f></x14:formula2>`
57
63
  : '') +
58
64
  `<xm:sqref>${escapeText(sqref)}</xm:sqref>`;
59
65
  return `<x14:dataValidation${ruleAttrs(rule)}>${body}</x14:dataValidation>`;
60
66
  }
61
- export function dataValidationPass() {
67
+ function operandText(operand, formulaNames) {
68
+ return escapeText(mangleFormula(stripFormulaEquals(operand), formulaNames));
69
+ }
70
+ const FORMULA_ELEMENTS = new Set(['formula1', 'formula2']);
71
+ export function dataValidationPass(definedNames) {
62
72
  const entries = [];
63
73
  let current;
64
- let slot;
74
+ const formula = new TextCapture(FORMULA_ELEMENTS);
65
75
  const handlers = {
66
- onOpen(name, attrs) {
76
+ onOpen(name, attrs, selfClosing, scope) {
67
77
  const ln = localName(name);
68
- if (ln === 'dataValidation' && !name.includes(':')) {
78
+ if (ln === 'dataValidation' && isMainNamespaceElement(scope, name)) {
69
79
  current = { attrs, formulae: [] };
70
80
  }
71
- else if (current !== undefined && ln === 'formula1') {
72
- slot = 0;
73
- current.formulae[0] = '';
74
- }
75
- else if (current !== undefined && ln === 'formula2') {
76
- slot = 1;
77
- current.formulae[1] = '';
81
+ else if (current !== undefined) {
82
+ formula.open(ln, selfClosing);
78
83
  }
79
84
  },
80
85
  onText(text) {
81
- if (current !== undefined && slot !== undefined) {
82
- current.formulae[slot] = (current.formulae[slot] ?? '') + text;
83
- }
86
+ formula.text(text);
84
87
  },
85
88
  onClose(name) {
86
89
  const ln = localName(name);
87
- if (ln === 'formula1' || ln === 'formula2') {
88
- slot = undefined;
90
+ const text = formula.close(ln);
91
+ if (text !== undefined) {
92
+ if (current !== undefined)
93
+ current.formulae[ln === 'formula1' ? 0 : 1] = text;
89
94
  return;
90
95
  }
91
96
  if (ln === 'dataValidation' && current !== undefined) {
92
- const built = buildEntry(current.attrs, current.formulae);
97
+ const built = buildEntry(current.attrs, current.formulae, definedNames);
93
98
  if (built !== undefined)
94
99
  entries.push(built);
95
100
  current = undefined;
@@ -98,14 +103,14 @@ export function dataValidationPass() {
98
103
  };
99
104
  return { handlers, result: () => entries };
100
105
  }
101
- function buildEntry(attrs, formulae) {
106
+ function buildEntry(attrs, formulae, definedNames) {
102
107
  const { sqref } = attrs;
103
108
  if (sqref === undefined)
104
109
  return undefined;
105
- const rule = buildRule(attrs, formulae);
110
+ const rule = buildRule(attrs, formulae, definedNames);
106
111
  return rule === undefined ? undefined : { sqref, rule };
107
112
  }
108
- function buildRule(attrs, formulae) {
113
+ function buildRule(attrs, formulae, definedNames) {
109
114
  const { type } = attrs;
110
115
  if (type === undefined || !isDataValidationType(type))
111
116
  return undefined;
@@ -119,12 +124,17 @@ function buildRule(attrs, formulae) {
119
124
  }
120
125
  if (boolStrict(attrs.allowBlank))
121
126
  rule.allowBlank = true;
127
+ if (boolStrict(attrs.showDropDown))
128
+ rule.suppressDropDown = true;
122
129
  if (boolStrict(attrs.showInputMessage))
123
130
  rule.showInputMessage = true;
124
131
  if (boolStrict(attrs.showErrorMessage))
125
132
  rule.showErrorMessage = true;
126
133
  if (attrs.errorStyle !== undefined && isDataValidationErrorStyle(attrs.errorStyle))
127
134
  rule.errorStyle = attrs.errorStyle;
135
+ const imeMode = enumToken(attrs.imeMode, isDataValidationImeMode);
136
+ if (imeMode !== undefined)
137
+ rule.imeMode = imeMode;
128
138
  if (attrs.error !== undefined)
129
139
  rule.error = attrs.error;
130
140
  if (attrs.errorTitle !== undefined)
@@ -135,27 +145,28 @@ function buildRule(attrs, formulae) {
135
145
  rule.promptTitle = attrs.promptTitle;
136
146
  const parsed = formulae
137
147
  .filter((f) => f !== undefined)
148
+ .map((f) => unmangleFunctions(f, definedNames))
138
149
  .map((f) => (type === 'list' || type === 'custom' ? f : coerceNumericLiteral(f)));
139
150
  if (parsed.length > 0)
140
151
  rule.formulae = parsed;
141
152
  return rule;
142
153
  }
143
- export function extendedDataValidationPass() {
154
+ export function extendedDataValidationPass(definedNames) {
144
155
  const entries = [];
145
156
  let current;
146
157
  let slot;
147
158
  const capture = new TextCapture(['f', 'sqref']);
148
159
  const handlers = {
149
- onOpen(name, attrs, selfClosing) {
160
+ onOpen(name, attrs, selfClosing, scope) {
150
161
  const ln = localName(name);
151
- const prefixed = name.includes(':');
152
- if (ln === 'dataValidation' && prefixed) {
162
+ const extension = isExtensionElement(scope, name);
163
+ if (ln === 'dataValidation' && extension) {
153
164
  current = { attrs, formulae: [], sqref: '' };
154
165
  }
155
- else if (current !== undefined && prefixed && ln === 'formula1') {
166
+ else if (current !== undefined && extension && ln === 'formula1') {
156
167
  slot = 0;
157
168
  }
158
- else if (current !== undefined && prefixed && ln === 'formula2') {
169
+ else if (current !== undefined && extension && ln === 'formula2') {
159
170
  slot = 1;
160
171
  }
161
172
  else if (current !== undefined) {
@@ -165,8 +176,9 @@ export function extendedDataValidationPass() {
165
176
  onText(chunk) {
166
177
  capture.text(chunk);
167
178
  },
168
- onClose(name) {
179
+ onClose(name, scope) {
169
180
  const ln = localName(name);
181
+ const extension = isExtensionElement(scope, name);
170
182
  const text = capture.close(ln);
171
183
  if (text !== undefined) {
172
184
  if (ln === 'f') {
@@ -177,11 +189,11 @@ export function extendedDataValidationPass() {
177
189
  current.sqref = text;
178
190
  }
179
191
  }
180
- else if ((ln === 'formula1' || ln === 'formula2') && name.includes(':')) {
192
+ else if ((ln === 'formula1' || ln === 'formula2') && extension) {
181
193
  slot = undefined;
182
194
  }
183
- else if (ln === 'dataValidation' && name.includes(':') && current !== undefined) {
184
- const built = buildExtendedEntry(current.attrs, current.formulae, current.sqref);
195
+ else if (ln === 'dataValidation' && extension && current !== undefined) {
196
+ const built = buildExtendedEntry(current.attrs, current.formulae, current.sqref, definedNames);
185
197
  if (built !== undefined)
186
198
  entries.push(built);
187
199
  current = undefined;
@@ -190,16 +202,18 @@ export function extendedDataValidationPass() {
190
202
  };
191
203
  return { handlers, result: () => entries };
192
204
  }
193
- function buildExtendedEntry(attrs, formulae, sqref) {
205
+ function buildExtendedEntry(attrs, formulae, sqref, definedNames) {
194
206
  if (sqref === '')
195
207
  return undefined;
196
- const rule = buildRule(attrs, formulae);
208
+ const rule = buildRule(attrs, formulae, definedNames);
197
209
  return rule === undefined ? undefined : { sqref, rule, extended: true };
198
210
  }
199
211
  export function applyDataValidations(sheet, entries) {
200
212
  for (const { sqref, rule, extended } of entries) {
201
213
  if (decodeSqrefRects(sqref).length === 0)
202
214
  continue;
203
- sheet.addDataValidation(sqref, rule, extended ? { extended: true } : {});
215
+ admitting(() => {
216
+ sheet.addDataValidation(sqref, rule, extended ? { extended: true } : {});
217
+ });
204
218
  }
205
219
  }
@@ -1,4 +1,5 @@
1
1
  import { type VbaLibraryReference } from '../../vba/project-editor.ts';
2
+ import { type ReadPackageOptions } from '../opc/read-options.ts';
2
3
  /**
3
4
  * Remove a standard module from an existing macro-enabled package's VBA project, returning new package
4
5
  * bytes. Every part but `xl/vbaProject.bin` is preserved byte-for-byte (see {@link removeVbaModule} for
@@ -8,8 +9,10 @@ import { type VbaLibraryReference } from '../../vba/project-editor.ts';
8
9
  * @throws {VbaAuthorError} if the package carries no VBA project, `name` is not in the project, or names
9
10
  * a `document`/`designer` module.
10
11
  * @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
12
+ * @throws {PackageReadError} if the input is not a readable ZIP, or exceeds the inflate bound
13
+ * ({@link ReadPackageOptions.maxUncompressedBytes}, defaulting as `readXlsx` does).
11
14
  */
12
- export declare function editXlsxVbaRemoveModule(xlsx: Uint8Array, name: string): Uint8Array;
15
+ export declare function editXlsxVbaRemoveModule(xlsx: Uint8Array, name: string, options?: ReadPackageOptions): Uint8Array;
13
16
  /**
14
17
  * Add a registered (COM type-library) reference to an existing macro-enabled package's VBA project,
15
18
  * returning new package bytes. Every part but `xl/vbaProject.bin` is preserved byte-for-byte (see
@@ -19,5 +22,7 @@ export declare function editXlsxVbaRemoveModule(xlsx: Uint8Array, name: string):
19
22
  * @throws {VbaAuthorError} if the package carries no VBA project, or any field of `ref` is invalid (see
20
23
  * {@link VbaLibraryReference}).
21
24
  * @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
25
+ * @throws {PackageReadError} if the input is not a readable ZIP, or exceeds the inflate bound
26
+ * ({@link ReadPackageOptions.maxUncompressedBytes}, defaulting as `readXlsx` does).
22
27
  */
23
- export declare function editXlsxVbaAddReference(xlsx: Uint8Array, ref: VbaLibraryReference): Uint8Array;
28
+ export declare function editXlsxVbaAddReference(xlsx: Uint8Array, ref: VbaLibraryReference, options?: ReadPackageOptions): Uint8Array;
@@ -1,79 +1,79 @@
1
- import { strFromU8, strToU8, unzipSync, zipSync } from 'fflate';
1
+ import { strToU8, zipSync } from 'fflate';
2
2
  import { VbaAuthorError } from '../../vba/errors.js';
3
3
  import { addVbaReference, removeVbaModule, } from '../../vba/project-editor.js';
4
+ import { elementRange } from '../../xml/xml-read.js';
4
5
  import { relsPathFor, resolveRelativePart } from '../opc/part-paths.js';
5
- import { parseRelationshipRecords, relationshipTargetByType } from '../opc/read-opc.js';
6
+ import { packageAccessors, parseRelationshipRecords, readPartRelationships, } from '../opc/read-opc.js';
7
+ import { DEFAULT_MAX_UNCOMPRESSED } from '../opc/read-options.js';
8
+ import { inflateSpreadsheetPackage } from '../opc/sniff-format.js';
6
9
  import { FIXED_ENTRY_MTIME } from '../opc/zip-mtime.js';
7
10
  const OFFICE_DOCUMENT_REL = 'officeDocument';
8
11
  const VBA_PROJECT_REL = 'vbaProject';
9
12
  const VBA_SIGNATURE_REL_INFIX = 'vbaProjectSignature';
10
- export function editXlsxVbaRemoveModule(xlsx, name) {
11
- return applyToVbaProjectPart(xlsx, (bin) => removeVbaModule(bin, name));
13
+ export function editXlsxVbaRemoveModule(xlsx, name, options = {}) {
14
+ return applyToVbaProjectPart(xlsx, options, (bin) => removeVbaModule(bin, name));
12
15
  }
13
- export function editXlsxVbaAddReference(xlsx, ref) {
14
- return applyToVbaProjectPart(xlsx, (bin) => addVbaReference(bin, ref));
16
+ export function editXlsxVbaAddReference(xlsx, ref, options = {}) {
17
+ return applyToVbaProjectPart(xlsx, options, (bin) => addVbaReference(bin, ref));
15
18
  }
16
- function applyToVbaProjectPart(xlsx, apply) {
17
- const files = Object.assign(Object.create(null), unzipSync(xlsx));
18
- const binPath = locateVbaProjectPart(files);
19
- const bin = binPath === undefined ? undefined : files[binPath];
20
- if (binPath === undefined || bin === undefined) {
19
+ function applyToVbaProjectPart(xlsx, options, apply) {
20
+ const files = inflateSpreadsheetPackage(xlsx, options.maxUncompressedBytes ?? DEFAULT_MAX_UNCOMPRESSED);
21
+ const pkg = packageAccessors(files);
22
+ const binPath = locateVbaProjectPart(pkg);
23
+ const binKey = binPath === undefined ? undefined : pkg.partKey(binPath);
24
+ const bin = binKey === undefined ? undefined : files[binKey];
25
+ if (binPath === undefined || binKey === undefined || bin === undefined) {
21
26
  throw new VbaAuthorError('package has no VBA project to edit');
22
27
  }
23
- files[binPath] = apply(bin);
24
- dropStaleSignature(files, binPath);
28
+ files[binKey] = apply(bin);
29
+ dropStaleSignature(files, pkg, binPath);
25
30
  return zipSync(files, { mtime: FIXED_ENTRY_MTIME });
26
31
  }
27
- function locateVbaProjectPart(files) {
28
- const rootRels = textPart(files, '_rels/.rels');
29
- if (rootRels === undefined)
32
+ function locateVbaProjectPart(pkg) {
33
+ const workbookPath = readPartRelationships('', pkg.partText).targetPath(OFFICE_DOCUMENT_REL);
34
+ if (workbookPath === undefined)
30
35
  return undefined;
31
- const workbookTarget = relationshipTargetByType(rootRels, OFFICE_DOCUMENT_REL);
32
- if (workbookTarget === undefined)
33
- return undefined;
34
- const workbookPath = resolveRelativePart('', workbookTarget);
35
- const workbookRels = textPart(files, relsPathFor(workbookPath));
36
- if (workbookRels === undefined)
37
- return undefined;
38
- const vbaTarget = relationshipTargetByType(workbookRels, VBA_PROJECT_REL);
39
- if (vbaTarget === undefined)
40
- return undefined;
41
- return resolveRelativePart(workbookPath, vbaTarget);
36
+ return readPartRelationships(workbookPath, pkg.partText).targetPath(VBA_PROJECT_REL);
42
37
  }
43
- function dropStaleSignature(files, binPath) {
44
- const binRelsPath = relsPathFor(binPath);
45
- const binRels = textPart(files, binRelsPath);
46
- if (binRels === undefined)
38
+ function dropStaleSignature(files, pkg, binPath) {
39
+ const binRelsKey = pkg.partKey(relsPathFor(binPath));
40
+ const binRels = binRelsKey === undefined ? undefined : pkg.partText(binRelsKey);
41
+ if (binRelsKey === undefined || binRels === undefined)
47
42
  return;
48
43
  const signatureRels = parseRelationshipRecords(binRels).filter((rel) => !rel.external && rel.type.includes(VBA_SIGNATURE_REL_INFIX));
49
44
  if (signatureRels.length === 0)
50
45
  return;
51
- let contentTypes = textPart(files, '[Content_Types].xml');
46
+ const contentTypesKey = pkg.partKey(CONTENT_TYPES_PART);
47
+ let contentTypes = contentTypesKey === undefined ? undefined : pkg.partText(contentTypesKey);
52
48
  let rels = binRels;
53
49
  for (const rel of signatureRels) {
54
50
  const partPath = resolveRelativePart(binPath, rel.target);
55
- delete files[partPath];
56
- rels = removeRelationshipById(rels, rel.id);
57
- if (contentTypes !== undefined)
58
- contentTypes = removeContentTypeOverride(contentTypes, partPath);
51
+ const partKey = pkg.partKey(partPath);
52
+ if (partKey !== undefined)
53
+ delete files[partKey];
54
+ const partName = `/${partPath}`.toLowerCase();
55
+ rels = removeElements(rels, 'Relationship', (attrs) => attrs.Id === rel.id);
56
+ if (contentTypes !== undefined) {
57
+ contentTypes = removeElements(contentTypes, 'Override', (attrs) => attrs.PartName?.toLowerCase() === partName);
58
+ }
59
59
  }
60
60
  if (parseRelationshipRecords(rels).length === 0)
61
- delete files[binRelsPath];
61
+ delete files[binRelsKey];
62
62
  else
63
- files[binRelsPath] = strToU8(rels);
64
- if (contentTypes !== undefined)
65
- files['[Content_Types].xml'] = strToU8(contentTypes);
66
- }
67
- function textPart(files, path) {
68
- const bytes = files[path];
69
- return bytes === undefined ? undefined : strFromU8(bytes);
70
- }
71
- function removeRelationshipById(xml, id) {
72
- return xml.replace(/<Relationship\b[^>]*?\/>|<Relationship\b[\s\S]*?<\/Relationship>/g, (element) => (new RegExp(`\\bId="${escapeRegExp(id)}"`).test(element) ? '' : element));
73
- }
74
- function removeContentTypeOverride(xml, partPath) {
75
- return xml.replace(/<Override\b[^>]*?\/>/g, (element) => element.includes(`PartName="/${partPath}"`) ? '' : element);
63
+ files[binRelsKey] = strToU8(rels);
64
+ if (contentTypesKey !== undefined && contentTypes !== undefined) {
65
+ files[contentTypesKey] = strToU8(contentTypes);
66
+ }
76
67
  }
77
- function escapeRegExp(value) {
78
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
68
+ const CONTENT_TYPES_PART = '[Content_Types].xml';
69
+ function removeElements(xml, local, drop) {
70
+ let kept = '';
71
+ let rest = xml;
72
+ let range = elementRange(rest, [local]);
73
+ while (range !== undefined) {
74
+ kept += rest.slice(0, drop(range.attrs) ? range.start : range.end);
75
+ rest = rest.slice(range.end);
76
+ range = elementRange(rest, [local]);
77
+ }
78
+ return kept + rest;
79
79
  }
@@ -0,0 +1,7 @@
1
+ import { type Font } from '../../core/style.ts';
2
+ import { type XmlAttributes } from '../../xml/xml-scan.ts';
3
+ export type FontDraft = {
4
+ -readonly [K in keyof Font]?: Font[K];
5
+ };
6
+ export declare function applyFontChild(draft: FontDraft, local: string, attrs: XmlAttributes): void;
7
+ export declare function fontXml(font: Font, nameTag?: 'name' | 'rFont'): string;
@@ -0,0 +1,104 @@
1
+ import { isFontScheme, isFontVerticalAlignment, isNamedUnderlineStyle, } from '../../core/style.js';
2
+ import { numFinite, numInteger } from '../../xml/xml-attrs.js';
3
+ import { boolPresent } from '../../xml/xml-scan.js';
4
+ import { checkedToken, escapeAttr, numberText } from '../../xml/xml.js';
5
+ import { colorAttrs, parseColor } from './color-xml.js';
6
+ export function applyFontChild(draft, local, attrs) {
7
+ switch (local) {
8
+ case 'b':
9
+ draft.bold = boolPresent(attrs.val);
10
+ break;
11
+ case 'i':
12
+ draft.italic = boolPresent(attrs.val);
13
+ break;
14
+ case 'strike':
15
+ draft.strike = boolPresent(attrs.val);
16
+ break;
17
+ case 'outline':
18
+ draft.outline = boolPresent(attrs.val);
19
+ break;
20
+ case 'u':
21
+ draft.underline =
22
+ attrs.val === undefined
23
+ ? true
24
+ : attrs.val === 'none'
25
+ ? false
26
+ : isNamedUnderlineStyle(attrs.val)
27
+ ? attrs.val
28
+ : true;
29
+ break;
30
+ case 'vertAlign':
31
+ if (attrs.val !== undefined && isFontVerticalAlignment(attrs.val))
32
+ draft.vertAlign = attrs.val;
33
+ break;
34
+ case 'sz': {
35
+ const size = numFinite(attrs.val);
36
+ if (size !== undefined)
37
+ draft.size = size;
38
+ break;
39
+ }
40
+ case 'color':
41
+ draft.color = parseColor(attrs);
42
+ break;
43
+ case 'name':
44
+ case 'rFont':
45
+ if (attrs.val !== undefined)
46
+ draft.name = attrs.val;
47
+ break;
48
+ case 'family': {
49
+ const family = numInteger(attrs.val);
50
+ if (family !== undefined)
51
+ draft.family = family;
52
+ break;
53
+ }
54
+ case 'charset': {
55
+ const charset = numInteger(attrs.val);
56
+ if (charset !== undefined)
57
+ draft.charset = charset;
58
+ break;
59
+ }
60
+ case 'scheme':
61
+ if (attrs.val !== undefined && isFontScheme(attrs.val))
62
+ draft.scheme = attrs.val;
63
+ break;
64
+ default:
65
+ break;
66
+ }
67
+ }
68
+ export function fontXml(font, nameTag = 'name') {
69
+ const parts = [];
70
+ if (font.bold)
71
+ parts.push('<b/>');
72
+ if (font.italic)
73
+ parts.push('<i/>');
74
+ if (font.strike)
75
+ parts.push('<strike/>');
76
+ if (font.outline)
77
+ parts.push('<outline/>');
78
+ const underline = underlineXml(font.underline);
79
+ if (underline !== '')
80
+ parts.push(underline);
81
+ if (font.vertAlign !== undefined) {
82
+ parts.push(`<vertAlign val="${checkedToken(font.vertAlign, isFontVerticalAlignment, 'font vertical alignment')}"/>`);
83
+ }
84
+ if (font.size !== undefined)
85
+ parts.push(`<sz val="${numberText(font.size)}"/>`);
86
+ if (font.color !== undefined)
87
+ parts.push(`<color ${colorAttrs(font.color)}/>`);
88
+ if (font.name !== undefined)
89
+ parts.push(`<${nameTag} val="${escapeAttr(font.name)}"/>`);
90
+ if (font.family !== undefined)
91
+ parts.push(`<family val="${numberText(font.family)}"/>`);
92
+ if (font.charset !== undefined)
93
+ parts.push(`<charset val="${numberText(font.charset)}"/>`);
94
+ if (font.scheme !== undefined && font.scheme !== 'none')
95
+ parts.push(`<scheme val="${checkedToken(font.scheme, isFontScheme, 'font scheme')}"/>`);
96
+ return parts.join('');
97
+ }
98
+ function underlineXml(underline) {
99
+ if (underline === undefined || underline === false || underline === 'none')
100
+ return '';
101
+ if (underline === true || underline === 'single')
102
+ return '<u/>';
103
+ return `<u val="${checkedToken(underline, isNamedUnderlineStyle, 'underline style')}"/>`;
104
+ }
@@ -1,29 +1,26 @@
1
+ import type { Hyperlink } from '../../core/hyperlink.ts';
1
2
  import type { Worksheet } from '../../core/worksheet.ts';
2
3
  import { type CollectingPass } from '../../xml/xml-read.ts';
3
- import type { SheetRelIds } from './package-plan.ts';
4
- /** A hyperlink gathered from a sheet for serialisation: the cell it sits on, its target, and an
5
- * optional tooltip. The visible label is the cell's own value and is serialised as that value. */
6
- export interface CollectedHyperlink {
7
- readonly ref: string;
8
- readonly target: string;
9
- readonly tooltip?: string;
10
- }
4
+ import type { RelationshipLedger } from './package-plan.ts';
11
5
  /** A hyperlink resolved for serialisation. An external target carries a `relId` (the sheet
12
- * relationship holding the URL) plus that `target`; an internal target carries a `location` (the
13
- * in-workbook reference). Exactly one of `relId`/`location` is ever set. */
6
+ * relationship holding the URL); an internal target carries a `location` (the in-workbook
7
+ * reference). Exactly one of `relId`/`location` is ever set. */
14
8
  export interface HyperlinkPlan {
15
9
  readonly ref: string;
16
10
  readonly relId?: string;
17
- readonly target?: string;
18
11
  readonly location?: string;
19
12
  readonly tooltip?: string;
20
13
  }
21
- /** Gather every hyperlink cell on a sheet, in row-major order. */
22
- export declare function collectHyperlinks(sheet: Worksheet): CollectedHyperlink[];
23
- /** Split collected links into internal (location, no rel) and external (relationship) forms, drawing
24
- * each external link's relationship id from the sheet's allocator so external ids follow every other
25
- * sheet-local relationship in canonical order. An internal ('#'-prefixed) link consumes no id. */
26
- export declare function planHyperlinks(links: readonly CollectedHyperlink[], rels: SheetRelIds): HyperlinkPlan[];
14
+ /**
15
+ * Split a sheet's links into internal (location, no rel) and external (relationship) forms, recording
16
+ * each external link's URL in the sheet's ledger so its relationship follows every other sheet-local
17
+ * relationship in canonical order. An internal (`#`-prefixed) link records nothing.
18
+ *
19
+ * The links keep the order the sheet holds them in. That order is what decides which of two links over
20
+ * one cell `Worksheet.hyperlinkAt` reports, and a re-read adds them in document order, so writing them
21
+ * any other way would change the answer across a save.
22
+ */
23
+ export declare function planHyperlinks(links: readonly Hyperlink[], rels: RelationshipLedger): HyperlinkPlan[];
27
24
  /** The `<hyperlinks>` element, or '' when the sheet has none. Attribute order follows CT_Hyperlink:
28
25
  * `ref`, `r:id`, `location`, `tooltip`. */
29
26
  export declare function hyperlinksXml(links: readonly HyperlinkPlan[]): string;
@@ -38,9 +35,10 @@ interface ParsedHyperlink {
38
35
  /** A pass gathering every `<hyperlink>` element of a worksheet part, for a caller reading the part
39
36
  * alongside its other readers in one parse. */
40
37
  export declare function sheetHyperlinkPass(): CollectingPass<ParsedHyperlink[]>;
41
- /** Fold parsed hyperlinks onto a sheet's cells, wrapping each cell's existing value (its visible
42
- * label) into a {@link HyperlinkValue}. `targetOf` resolves a relationship id to its raw Target: a
43
- * URL for the external links hyperlinks almost always are, so it must stay unresolved against the
44
- * package rather than being handed over as a part path. */
38
+ /**
39
+ * Put parsed hyperlinks on a sheet, in document order, whatever the cells they cover hold. `targetOf`
40
+ * resolves a relationship id to its raw Target: a URL for the external links hyperlinks almost always
41
+ * are, so it must stay unresolved against the package rather than being handed over as a part path.
42
+ */
45
43
  export declare function applyHyperlinks(sheet: Worksheet, links: readonly ParsedHyperlink[], targetOf: (relId: string) => string | undefined): void;
46
44
  export {};