@borgar/fx 4.13.0 → 5.0.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 (141) hide show
  1. package/dist/index-BMr6cTgc.d.cts +1444 -0
  2. package/dist/index-BMr6cTgc.d.ts +1444 -0
  3. package/dist/index.cjs +3054 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +1 -0
  6. package/dist/index.d.ts +1 -0
  7. package/dist/index.js +2984 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/xlsx/index.cjs +3120 -0
  10. package/dist/xlsx/index.cjs.map +1 -0
  11. package/dist/xlsx/index.d.cts +55 -0
  12. package/dist/xlsx/index.d.ts +55 -0
  13. package/dist/xlsx/index.js +3049 -0
  14. package/dist/xlsx/index.js.map +1 -0
  15. package/docs/API.md +2959 -718
  16. package/docs/AST_format.md +2 -2
  17. package/eslint.config.mjs +40 -0
  18. package/lib/a1.spec.ts +32 -0
  19. package/lib/a1.ts +26 -0
  20. package/lib/addA1RangeBounds.ts +50 -0
  21. package/lib/addTokenMeta.spec.ts +166 -0
  22. package/lib/{addTokenMeta.js → addTokenMeta.ts} +53 -33
  23. package/lib/astTypes.ts +211 -0
  24. package/lib/cloneToken.ts +29 -0
  25. package/lib/{constants.js → constants.ts} +6 -3
  26. package/lib/fixRanges.spec.ts +220 -0
  27. package/lib/fixRanges.ts +260 -0
  28. package/lib/fromCol.spec.ts +15 -0
  29. package/lib/{fromCol.js → fromCol.ts} +1 -1
  30. package/lib/index.spec.ts +119 -0
  31. package/lib/index.ts +76 -0
  32. package/lib/isNodeType.ts +151 -0
  33. package/lib/isType.spec.ts +208 -0
  34. package/lib/{isType.js → isType.ts} +26 -25
  35. package/lib/lexers/{advRangeOp.js → advRangeOp.ts} +1 -1
  36. package/lib/lexers/{canEndRange.js → canEndRange.ts} +2 -2
  37. package/lib/lexers/{lexBoolean.js → lexBoolean.ts} +25 -6
  38. package/lib/lexers/{lexContext.js → lexContext.ts} +14 -6
  39. package/lib/lexers/{lexError.js → lexError.ts} +3 -3
  40. package/lib/lexers/{lexFunction.js → lexFunction.ts} +3 -2
  41. package/lib/lexers/lexNameFuncCntx.ts +112 -0
  42. package/lib/lexers/{lexNamed.js → lexNamed.ts} +4 -4
  43. package/lib/lexers/{lexNewLine.js → lexNewLine.ts} +3 -2
  44. package/lib/lexers/{lexNumber.js → lexNumber.ts} +4 -3
  45. package/lib/lexers/{lexOperator.js → lexOperator.ts} +5 -4
  46. package/lib/lexers/lexRange.ts +15 -0
  47. package/lib/lexers/{lexRangeA1.js → lexRangeA1.ts} +11 -7
  48. package/lib/lexers/{lexRangeR1C1.js → lexRangeR1C1.ts} +10 -6
  49. package/lib/lexers/{lexRangeTrim.js → lexRangeTrim.ts} +3 -2
  50. package/lib/lexers/{lexRefOp.js → lexRefOp.ts} +4 -3
  51. package/lib/lexers/{lexString.js → lexString.ts} +3 -3
  52. package/lib/lexers/{lexStructured.js → lexStructured.ts} +5 -5
  53. package/lib/lexers/{lexWhitespace.js → lexWhitespace.ts} +3 -2
  54. package/lib/lexers/sets.ts +51 -0
  55. package/lib/mergeRefTokens.spec.ts +141 -0
  56. package/lib/{mergeRefTokens.js → mergeRefTokens.ts} +14 -9
  57. package/lib/nodeTypes.ts +54 -0
  58. package/lib/parse.spec.ts +1410 -0
  59. package/lib/{parser.js → parse.ts} +81 -63
  60. package/lib/parseA1Range.spec.ts +233 -0
  61. package/lib/parseA1Range.ts +206 -0
  62. package/lib/parseA1Ref.spec.ts +337 -0
  63. package/lib/parseA1Ref.ts +115 -0
  64. package/lib/parseR1C1Range.ts +191 -0
  65. package/lib/parseR1C1Ref.spec.ts +323 -0
  66. package/lib/parseR1C1Ref.ts +127 -0
  67. package/lib/parseRef.spec.ts +90 -0
  68. package/lib/parseRef.ts +240 -0
  69. package/lib/{parseSRange.js → parseSRange.ts} +15 -10
  70. package/lib/parseStructRef.spec.ts +168 -0
  71. package/lib/parseStructRef.ts +76 -0
  72. package/lib/stringifyA1Range.spec.ts +72 -0
  73. package/lib/stringifyA1Range.ts +72 -0
  74. package/lib/stringifyA1Ref.spec.ts +64 -0
  75. package/lib/stringifyA1Ref.ts +59 -0
  76. package/lib/{stringifyPrefix.js → stringifyPrefix.ts} +17 -2
  77. package/lib/stringifyR1C1Range.spec.ts +92 -0
  78. package/lib/stringifyR1C1Range.ts +73 -0
  79. package/lib/stringifyR1C1Ref.spec.ts +63 -0
  80. package/lib/stringifyR1C1Ref.ts +67 -0
  81. package/lib/stringifyStructRef.spec.ts +124 -0
  82. package/lib/stringifyStructRef.ts +113 -0
  83. package/lib/stringifyTokens.ts +15 -0
  84. package/lib/toCol.spec.ts +11 -0
  85. package/lib/{toCol.js → toCol.ts} +4 -4
  86. package/lib/tokenTypes.ts +76 -0
  87. package/lib/tokenize-srefs.spec.ts +429 -0
  88. package/lib/tokenize.spec.ts +2103 -0
  89. package/lib/tokenize.ts +346 -0
  90. package/lib/translate.spec.ts +35 -0
  91. package/lib/translateToA1.spec.ts +247 -0
  92. package/lib/translateToA1.ts +231 -0
  93. package/lib/translateToR1C1.spec.ts +227 -0
  94. package/lib/translateToR1C1.ts +145 -0
  95. package/lib/types.ts +179 -0
  96. package/lib/xlsx/index.spec.ts +27 -0
  97. package/lib/xlsx/index.ts +32 -0
  98. package/package.json +45 -31
  99. package/tsconfig.json +28 -0
  100. package/typedoc-ignore-links.ts +17 -0
  101. package/typedoc.json +41 -0
  102. package/.eslintrc +0 -22
  103. package/benchmark/benchmark.js +0 -48
  104. package/benchmark/formulas.json +0 -15677
  105. package/dist/fx.d.ts +0 -823
  106. package/dist/fx.js +0 -2
  107. package/dist/package.json +0 -1
  108. package/lib/a1.js +0 -348
  109. package/lib/a1.spec.js +0 -458
  110. package/lib/addTokenMeta.spec.js +0 -153
  111. package/lib/astTypes.js +0 -96
  112. package/lib/extraTypes.js +0 -74
  113. package/lib/fixRanges.js +0 -104
  114. package/lib/fixRanges.spec.js +0 -171
  115. package/lib/fromCol.spec.js +0 -11
  116. package/lib/index.js +0 -134
  117. package/lib/index.spec.js +0 -67
  118. package/lib/isType.spec.js +0 -168
  119. package/lib/lexer-srefs.spec.js +0 -324
  120. package/lib/lexer.js +0 -264
  121. package/lib/lexer.spec.js +0 -1953
  122. package/lib/lexers/lexRange.js +0 -8
  123. package/lib/lexers/sets.js +0 -38
  124. package/lib/mergeRefTokens.spec.js +0 -121
  125. package/lib/package.json +0 -1
  126. package/lib/parseRef.js +0 -157
  127. package/lib/parseRef.spec.js +0 -71
  128. package/lib/parseStructRef.js +0 -48
  129. package/lib/parseStructRef.spec.js +0 -164
  130. package/lib/parser.spec.js +0 -1208
  131. package/lib/rc.js +0 -341
  132. package/lib/rc.spec.js +0 -403
  133. package/lib/stringifyStructRef.js +0 -80
  134. package/lib/stringifyStructRef.spec.js +0 -182
  135. package/lib/toCol.spec.js +0 -11
  136. package/lib/translate-toA1.spec.js +0 -214
  137. package/lib/translate-toRC.spec.js +0 -197
  138. package/lib/translate.js +0 -239
  139. package/lib/translate.spec.js +0 -21
  140. package/rollup.config.mjs +0 -22
  141. package/tsd.json +0 -12
@@ -0,0 +1,124 @@
1
+ import { describe, test, expect } from 'vitest';
2
+ import { stringifyStructRef, stringifyStructRefXlsx } from './stringifyStructRef.ts';
3
+
4
+ describe('serialize structured references', () => {
5
+ test('simple column references', () => {
6
+ expect(stringifyStructRef({
7
+ columns: [ 'foo' ]
8
+ })).toBe('[foo]');
9
+
10
+ expect(stringifyStructRef({
11
+ columns: [ 'foo' ],
12
+ table: 'tableName'
13
+ })).toBe('tableName[foo]');
14
+
15
+ expect(stringifyStructRef({
16
+ columns: [ 'lorem ipsum' ],
17
+ table: 'tableName'
18
+ })).toBe('tableName[lorem ipsum]');
19
+ });
20
+
21
+ test('column range references', () => {
22
+ expect(stringifyStructRef({
23
+ columns: [ 'foo', 'sævör' ],
24
+ table: 'tableName'
25
+ })).toBe('tableName[[foo]:[sævör]]');
26
+
27
+ expect(stringifyStructRef({
28
+ columns: [ 'lorem ipsum', 'sævör' ],
29
+ table: 'tableName'
30
+ })).toBe('tableName[[lorem ipsum]:[sævör]]');
31
+ });
32
+
33
+ test('section references', () => {
34
+ expect(stringifyStructRef({
35
+ sections: [ 'data' ],
36
+ table: 'tableName'
37
+ })).toBe('tableName[#Data]');
38
+
39
+ expect(stringifyStructRef({
40
+ sections: [ 'data', 'headers' ],
41
+ table: 'table'
42
+ })).toBe('table[[#Data],[#Headers]]');
43
+ });
44
+
45
+ test('complex references with context', () => {
46
+ expect(stringifyStructRef({
47
+ columns: [ 'my column', 'fo@o' ],
48
+ sections: [ 'data' ],
49
+ table: 'tableName',
50
+ context: [ 'workbook.xlsx' ]
51
+ })).toBe('workbook.xlsx!tableName[[#Data],[my column]:[fo\'@o]]');
52
+ });
53
+
54
+ test('this row references', () => {
55
+ expect(stringifyStructRef({
56
+ columns: [ 'bar' ],
57
+ sections: [ 'this row' ],
58
+ table: 'foo'
59
+ })).toBe('foo[@bar]');
60
+
61
+ expect(stringifyStructRef({
62
+ columns: [ 'bar', 'baz' ],
63
+ sections: [ 'this row' ],
64
+ table: 'foo'
65
+ })).toBe('foo[@[bar]:[baz]]');
66
+
67
+ expect(stringifyStructRef({
68
+ columns: [ 'lorem ipsum', 'baz' ],
69
+ sections: [ 'this row' ],
70
+ table: 'foo'
71
+ })).toBe('foo[@[lorem ipsum]:[baz]]');
72
+ });
73
+ });
74
+
75
+ describe('structured references serialize in xlsx mode', () => {
76
+ test('context vs workbookName/sheetName handling', () => {
77
+ expect(stringifyStructRefXlsx({
78
+ // @ts-expect-error -- this is testing invalid input
79
+ context: [ 'Lorem', 'Ipsum' ],
80
+ columns: [ 'foo' ]
81
+ })).toBe('[foo]');
82
+
83
+ expect(stringifyStructRef({
84
+ // @ts-expect-error -- this is testing invalid input
85
+ workbookName: 'Lorem',
86
+ sheetName: 'Ipsum',
87
+ columns: [ 'foo' ]
88
+ })).toBe('[foo]');
89
+
90
+ expect(stringifyStructRefXlsx({
91
+ workbookName: 'Lorem',
92
+ sheetName: 'Ipsum',
93
+ columns: [ 'foo' ]
94
+ })).toBe('[Lorem]Ipsum![foo]');
95
+ });
96
+
97
+ test('workbook and sheet name handling', () => {
98
+ expect(stringifyStructRefXlsx({
99
+ workbookName: 'Lorem',
100
+ columns: [ 'foo' ]
101
+ })).toBe('[Lorem]![foo]');
102
+
103
+ expect(stringifyStructRefXlsx({
104
+ sheetName: 'Ipsum',
105
+ columns: [ 'foo' ]
106
+ })).toBe('Ipsum![foo]');
107
+ });
108
+ });
109
+
110
+ describe('longform serialize (in xlsx mode)', () => {
111
+ test('thisRow option affects output format', () => {
112
+ expect(stringifyStructRefXlsx({
113
+ table: 'Table2',
114
+ columns: [ 'col1' ],
115
+ sections: [ 'this row' ]
116
+ }, { thisRow: true })).toBe('Table2[[#This row],[col1]]');
117
+
118
+ expect(stringifyStructRef({
119
+ table: 'Table2',
120
+ columns: [ 'col1' ],
121
+ sections: [ 'this row' ]
122
+ }, { thisRow: true })).toBe('Table2[[#This row],[col1]]');
123
+ });
124
+ });
@@ -0,0 +1,113 @@
1
+ import type { ReferenceStruct, ReferenceStructXlsx } from './types.ts';
2
+ import { stringifyPrefix, stringifyPrefixXlsx } from './stringifyPrefix.ts';
3
+
4
+ function quoteColname (str: string): string {
5
+ return str.replace(/([[\]#'@])/g, '\'$1');
6
+ }
7
+
8
+ function needsBraces (str: string): boolean {
9
+ return /[^a-zA-Z0-9\u00a1-\uffff]/.test(str);
10
+ }
11
+
12
+ function toSentenceCase (str: string): string {
13
+ return str[0].toUpperCase() + str.slice(1).toLowerCase();
14
+ }
15
+
16
+ export function stringifySRef (refObject: ReferenceStruct, thisRow = false) {
17
+ let s = '';
18
+ if (refObject.table) {
19
+ s += refObject.table;
20
+ }
21
+ const numColumns = refObject.columns?.length ?? 0;
22
+ const numSections = refObject.sections?.length ?? 0;
23
+ // single section
24
+ if (numSections === 1 && !numColumns) {
25
+ s += `[#${toSentenceCase(refObject.sections[0])}]`;
26
+ }
27
+ // single column
28
+ else if (!numSections && numColumns === 1) {
29
+ s += `[${quoteColname(refObject.columns[0])}]`;
30
+ }
31
+ else {
32
+ s += '[';
33
+ // single [#this row] sections get normalized to an @ by default
34
+ const singleAt = !thisRow && numSections === 1 && refObject.sections[0].toLowerCase() === 'this row';
35
+ if (singleAt) {
36
+ s += '@';
37
+ }
38
+ else if (numSections) {
39
+ s += refObject.sections
40
+ .map(d => `[#${toSentenceCase(d)}]`)
41
+ .join(',');
42
+ if (numColumns) {
43
+ s += ',';
44
+ }
45
+ }
46
+ // a case of a single alphanumberic column with a [#this row] becomes [@col]
47
+ if (singleAt && refObject.columns.length === 1 && !needsBraces(refObject.columns[0])) {
48
+ s += quoteColname(refObject.columns[0]);
49
+ }
50
+ else if (numColumns) {
51
+ s += refObject.columns.slice(0, 2)
52
+ .map(d => (`[${quoteColname(d)}]`))
53
+ .join(':');
54
+ }
55
+ s += ']';
56
+ }
57
+ return s;
58
+ }
59
+
60
+ /**
61
+ * Options for {@link stringifyStructRef}
62
+ */
63
+ export type OptsStringifyStructRef = {
64
+ /**
65
+ * Enforces using the `[#This Row]` instead of the `@` shorthand when serializing structured ranges.
66
+ * @defaultValue false
67
+ */
68
+ thisRow?: boolean;
69
+ };
70
+
71
+ /**
72
+ * Returns a string representation of a structured reference object.
73
+ *
74
+ * ```js
75
+ * stringifyStructRef({
76
+ * context: [ 'workbook.xlsx' ],
77
+ * sections: [ 'data' ],
78
+ * columns: [ 'my column', '@foo' ],
79
+ * table: 'tableName',
80
+ * });
81
+ * // => 'workbook.xlsx!tableName[[#Data],[Column1]:[Column2]]'
82
+ * ```
83
+ *
84
+ * @see {@link OptsStringifyStructRef}
85
+ * @param refObject A structured reference object.
86
+ * @param [options={}] Options.
87
+ * @returns The given structured reference in string format.
88
+ */
89
+ export function stringifyStructRef (refObject: ReferenceStruct, options: OptsStringifyStructRef = {}): string {
90
+ return stringifyPrefix(refObject) + stringifySRef(refObject, !!options.thisRow);
91
+ }
92
+
93
+ /**
94
+ * Returns a string representation of a structured reference object.
95
+ *
96
+ * ```js
97
+ * stringifyStructRef({
98
+ * workbookName: 'workbook.xlsx',
99
+ * sheetName: '',
100
+ * sections: [ 'data' ],
101
+ * columns: [ 'my column', '@foo' ],
102
+ * table: 'tableName',
103
+ * });
104
+ * // => 'workbook.xlsx!tableName[[#Data],[Column1]:[Column2]]'
105
+ * ```
106
+ *
107
+ * @param refObject A structured reference object.
108
+ * @param [options] Options.
109
+ * @returns The given structured reference in string format.
110
+ */
111
+ export function stringifyStructRefXlsx (refObject: ReferenceStructXlsx, options: OptsStringifyStructRef = {}): string {
112
+ return stringifyPrefixXlsx(refObject) + stringifySRef(refObject, !!options.thisRow);
113
+ }
@@ -0,0 +1,15 @@
1
+ import type { Token } from './types.ts';
2
+
3
+ /**
4
+ * Collapses a list of tokens into a formula string.
5
+ *
6
+ * @param tokens A list of tokens.
7
+ * @returns A formula string.
8
+ */
9
+ export function stringifyTokens (tokens: Token[]): string {
10
+ let s = '';
11
+ for (const token of tokens) {
12
+ s += token.value;
13
+ }
14
+ return s;
15
+ }
@@ -0,0 +1,11 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { toCol } from './toCol.ts';
3
+
4
+ describe('toCol', () => {
5
+ it('toCol converts integers to column ids', () => {
6
+ expect(toCol(0)).toBe('A');
7
+ expect(toCol(26)).toBe('AA');
8
+ expect(toCol(701)).toBe('ZZ');
9
+ expect(toCol(18277)).toBe('ZZZ');
10
+ });
11
+ });
@@ -2,15 +2,15 @@ const charFrom = String.fromCharCode;
2
2
 
3
3
  /**
4
4
  * Convert a 0 based offset number to a column string
5
- * representation (`2` = `"C"`).
5
+ * representation (`0` = `"A"`, `2` = `"C"`).
6
6
  *
7
7
  * The method expects a number between 0 and 16383. Other input will
8
8
  * return garbage.
9
9
  *
10
- * @param {number} columnIndex Zero based column index number
11
- * @returns {string} The column string identifier
10
+ * @param columnIndex Zero based column index number
11
+ * @returns The column string identifier
12
12
  */
13
- export function toCol (columnIndex) {
13
+ export function toCol (columnIndex: number): string {
14
14
  return (
15
15
  (columnIndex >= 702
16
16
  ? charFrom(((((columnIndex - 702) / 676) - 0) % 26) + 65)
@@ -0,0 +1,76 @@
1
+ import {
2
+ OPERATOR,
3
+ BOOLEAN,
4
+ ERROR,
5
+ NUMBER,
6
+ FUNCTION,
7
+ NEWLINE,
8
+ WHITESPACE,
9
+ STRING,
10
+ CONTEXT,
11
+ CONTEXT_QUOTE,
12
+ REF_RANGE,
13
+ REF_BEAM,
14
+ REF_TERNARY,
15
+ REF_NAMED,
16
+ REF_STRUCT,
17
+ FX_PREFIX,
18
+ UNKNOWN
19
+ } from './constants.ts';
20
+
21
+ /**
22
+ * A dictionary of the types used to identify token variants.
23
+ * @prop OPERATOR - Unary or binary operator (`+`, `%`)
24
+ * @prop BOOLEAN - Boolean literal (`TRUE`)
25
+ * @prop ERROR - Error literal (`#VALUE!`)
26
+ * @prop NUMBER - Number literal (`123.4`, `-1.5e+2`)
27
+ * @prop FUNCTION - Function name (`SUM`)
28
+ * @prop NEWLINE - Newline character (`\n`)
29
+ * @prop WHITESPACE - Whitespace character sequence (` `)
30
+ * @prop STRING - String literal (`"Lorem ipsum"`)
31
+ * @prop CONTEXT - Reference context ([Workbook.xlsx]Sheet1)
32
+ * @prop CONTEXT_QUOTE - Quoted reference context (`'[My workbook.xlsx]Sheet1'`)
33
+ * @prop REF_RANGE - A range identifier (`A1`)
34
+ * @prop REF_BEAM - A range "beam" identifier (`A:A` or `1:1`)
35
+ * @prop REF_TERNARY - A ternary range identifier (`B2:B`)
36
+ * @prop REF_NAMED - A name / named range identifier (`income`)
37
+ * @prop REF_STRUCT - A structured reference identifier (`table[[Column1]:[Column2]]`)
38
+ * @prop FX_PREFIX - A leading equals sign at the start of a formula (`=`)
39
+ * @prop UNKNOWN - Any unidentifiable range of characters.
40
+ */
41
+ export const tokenTypes = Object.freeze({
42
+ /** Unary or binary operator (`+`, `%`) */
43
+ OPERATOR: OPERATOR,
44
+ /** Boolean literal (`TRUE`) */
45
+ BOOLEAN: BOOLEAN,
46
+ /** Error literal (`#VALUE!`) */
47
+ ERROR: ERROR,
48
+ /** Number literal (`123.4`, `-1.5e+2`) */
49
+ NUMBER: NUMBER,
50
+ /** Function name (`SUM`) */
51
+ FUNCTION: FUNCTION,
52
+ /** Newline character (`\n`) */
53
+ NEWLINE: NEWLINE,
54
+ /** Whitespace character sequence (` `) */
55
+ WHITESPACE: WHITESPACE,
56
+ /** String literal (`"Lorem ipsum"`) */
57
+ STRING: STRING,
58
+ /** Reference context ([Workbook.xlsx]Sheet1) */
59
+ CONTEXT: CONTEXT,
60
+ /** Quoted reference context (`'[My workbook.xlsx]Sheet1'`) */
61
+ CONTEXT_QUOTE: CONTEXT_QUOTE,
62
+ /** A range identifier (`A1`) */
63
+ REF_RANGE: REF_RANGE,
64
+ /** A range "beam" identifier (`A:A` or `1:1`) */
65
+ REF_BEAM: REF_BEAM,
66
+ /** A ternary range identifier (`B2:B`) */
67
+ REF_TERNARY: REF_TERNARY,
68
+ /** A name / named range identifier (`income`) */
69
+ REF_NAMED: REF_NAMED,
70
+ /** A structured reference identifier (`table[[Column1]:[Column2]]`) */
71
+ REF_STRUCT: REF_STRUCT,
72
+ /** A leading equals sign at the start of a formula (`=`) */
73
+ FX_PREFIX: FX_PREFIX,
74
+ /** Any unidentifiable range of characters. */
75
+ UNKNOWN: UNKNOWN
76
+ });