@borgar/fx 4.10.2 → 4.11.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/dist/fx.js +2 -2
- package/lib/a1.js +2 -56
- package/lib/a1.spec.js +0 -11
- package/lib/addTokenMeta.js +1 -1
- package/lib/fixRanges.js +2 -1
- package/lib/fromCol.js +32 -0
- package/lib/fromCol.spec.js +11 -0
- package/lib/index.js +5 -6
- package/lib/isType.spec.js +168 -0
- package/lib/lexerParts.js +2 -2
- package/lib/parseSRange.js +165 -0
- package/lib/parseStructRef.js +48 -0
- package/lib/parseStructRef.spec.js +160 -0
- package/lib/stringifyStructRef.js +80 -0
- package/lib/{sr.spec.js → stringifyStructRef.spec.js} +3 -151
- package/lib/toCol.js +23 -0
- package/lib/toCol.spec.js +11 -0
- package/package.json +2 -2
- package/rollup.config.mjs +5 -5
- package/lib/sr.js +0 -293
package/lib/sr.js
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
import { parseRef } from './parseRef.js';
|
|
2
|
-
import { stringifyPrefix, stringifyPrefixAlt } from './stringifyPrefix.js';
|
|
3
|
-
|
|
4
|
-
const re_SRcolumnB = /^\[('['#@[\]]|[^'#@[\]])+\]/i;
|
|
5
|
-
const re_SRcolumnN = /^([^#@[\]:]+)/i;
|
|
6
|
-
|
|
7
|
-
const keyTerms = {
|
|
8
|
-
'headers': 1,
|
|
9
|
-
'data': 2,
|
|
10
|
-
'totals': 4,
|
|
11
|
-
'all': 8,
|
|
12
|
-
'this row': 16,
|
|
13
|
-
'@': 16
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const fz = (...a) => Object.freeze(a);
|
|
17
|
-
|
|
18
|
-
// only combinations allowed are: #data + (#headers | #totals | #data)
|
|
19
|
-
const sectionMap = {
|
|
20
|
-
// no terms
|
|
21
|
-
0: fz(),
|
|
22
|
-
// single term
|
|
23
|
-
1: fz('headers'),
|
|
24
|
-
2: fz('data'),
|
|
25
|
-
4: fz('totals'),
|
|
26
|
-
8: fz('all'),
|
|
27
|
-
16: fz('this row'),
|
|
28
|
-
// headers+data
|
|
29
|
-
3: fz('headers', 'data'),
|
|
30
|
-
// totals+data
|
|
31
|
-
6: fz('data', 'totals')
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const matchColumn = (s, allowUnbraced = true) => {
|
|
35
|
-
let m = re_SRcolumnB.exec(s);
|
|
36
|
-
if (m) {
|
|
37
|
-
const value = m[0].slice(1, -1).replace(/'(['#@[\]])/g, '$1');
|
|
38
|
-
return [ m[0], value ];
|
|
39
|
-
}
|
|
40
|
-
if (allowUnbraced) {
|
|
41
|
-
m = re_SRcolumnN.exec(s);
|
|
42
|
-
if (m) {
|
|
43
|
-
return [ m[0], m[0] ];
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return null;
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
export function parseSRange (raw) {
|
|
50
|
-
const columns = [];
|
|
51
|
-
let pos = 0;
|
|
52
|
-
let s = raw;
|
|
53
|
-
let m;
|
|
54
|
-
let m1;
|
|
55
|
-
let terms = 0;
|
|
56
|
-
|
|
57
|
-
// start of structured ref?
|
|
58
|
-
if ((m = /^(\[\s*)/.exec(s))) {
|
|
59
|
-
// quickly determine if this is a simple keyword or column
|
|
60
|
-
// [#keyword]
|
|
61
|
-
if ((m1 = /^\[#([a-z ]+)\]/i.exec(s))) {
|
|
62
|
-
const k = m1[1].toLowerCase();
|
|
63
|
-
pos += m1[0].length;
|
|
64
|
-
if (keyTerms[k]) {
|
|
65
|
-
terms |= keyTerms[k];
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
// [column]
|
|
72
|
-
else if ((m1 = matchColumn(s, false))) {
|
|
73
|
-
pos += m1[0].length;
|
|
74
|
-
columns.push(m1[1]);
|
|
75
|
-
}
|
|
76
|
-
// use the "normal" method
|
|
77
|
-
// [[#keyword]]
|
|
78
|
-
// [[column]]
|
|
79
|
-
// [@]
|
|
80
|
-
// [@column]
|
|
81
|
-
// [@[column]]
|
|
82
|
-
// [@column:column]
|
|
83
|
-
// [@column:[column]]
|
|
84
|
-
// [@[column]:column]
|
|
85
|
-
// [@[column]:[column]]
|
|
86
|
-
// [column:column]
|
|
87
|
-
// [column:[column]]
|
|
88
|
-
// [[column]:column]
|
|
89
|
-
// [[column]:[column]]
|
|
90
|
-
// [[#keyword],column]
|
|
91
|
-
// [[#keyword],column:column]
|
|
92
|
-
// [[#keyword],[#keyword],column:column]
|
|
93
|
-
// ...
|
|
94
|
-
else {
|
|
95
|
-
let expect_more = true;
|
|
96
|
-
s = s.slice(m[1].length);
|
|
97
|
-
pos += m[1].length;
|
|
98
|
-
// match keywords as we find them
|
|
99
|
-
while (
|
|
100
|
-
expect_more &&
|
|
101
|
-
(m = /^\[#([a-z ]+)\](\s*,\s*)?/i.exec(s))
|
|
102
|
-
) {
|
|
103
|
-
const k = m[1].toLowerCase();
|
|
104
|
-
if (keyTerms[k]) {
|
|
105
|
-
terms |= keyTerms[k];
|
|
106
|
-
s = s.slice(m[0].length);
|
|
107
|
-
pos += m[0].length;
|
|
108
|
-
expect_more = !!m[2];
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
return null;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
// is there an @ specifier?
|
|
115
|
-
if (expect_more && (m = /^@/.exec(s))) {
|
|
116
|
-
terms |= keyTerms['@'];
|
|
117
|
-
s = s.slice(1);
|
|
118
|
-
pos += 1;
|
|
119
|
-
expect_more = s[0] !== ']';
|
|
120
|
-
}
|
|
121
|
-
// not all keyword terms may be combined
|
|
122
|
-
if (!(terms in sectionMap)) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
// column definitions
|
|
126
|
-
const leftCol = expect_more ? matchColumn(raw.slice(pos)) : null;
|
|
127
|
-
if (leftCol) {
|
|
128
|
-
pos += leftCol[0].length;
|
|
129
|
-
columns.push(leftCol[1]);
|
|
130
|
-
s = raw.slice(pos);
|
|
131
|
-
if (s[0] === ':') {
|
|
132
|
-
s = s.slice(1);
|
|
133
|
-
pos++;
|
|
134
|
-
const rightCol = matchColumn(s);
|
|
135
|
-
if (rightCol) {
|
|
136
|
-
pos += rightCol[0].length;
|
|
137
|
-
columns.push(rightCol[1]);
|
|
138
|
-
}
|
|
139
|
-
else {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
expect_more = false;
|
|
144
|
-
}
|
|
145
|
-
// advance ws
|
|
146
|
-
while (raw[pos] === ' ') {
|
|
147
|
-
pos++;
|
|
148
|
-
}
|
|
149
|
-
// close the ref
|
|
150
|
-
if (expect_more || raw[pos] !== ']') {
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
// step over the closing ]
|
|
154
|
-
pos++;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
else {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const sections = sectionMap[terms];
|
|
162
|
-
return {
|
|
163
|
-
columns,
|
|
164
|
-
sections: sections ? sections.concat() : sections,
|
|
165
|
-
length: pos,
|
|
166
|
-
token: raw.slice(0, pos)
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Parse a structured reference string into an object representing it.
|
|
172
|
-
*
|
|
173
|
-
* ```js
|
|
174
|
-
* parseStructRef('workbook.xlsx!tableName[[#Data],[Column1]:[Column2]]');
|
|
175
|
-
* // => {
|
|
176
|
-
* // context: [ 'workbook.xlsx' ],
|
|
177
|
-
* // sections: [ 'data' ],
|
|
178
|
-
* // columns: [ 'my column', '@foo' ],
|
|
179
|
-
* // table: 'tableName',
|
|
180
|
-
* // }
|
|
181
|
-
* ```
|
|
182
|
-
*
|
|
183
|
-
* For A:A or A1:A style ranges, `null` will be used for any dimensions that the
|
|
184
|
-
* syntax does not specify:
|
|
185
|
-
*
|
|
186
|
-
* @tutorial References.md
|
|
187
|
-
* @param {string} ref A structured reference string
|
|
188
|
-
* @param {object} [options={}] Options
|
|
189
|
-
* @param {boolean} [options.xlsx=false] Switches to the `[1]Sheet1!A1` or `[1]!name` prefix syntax form for external workbooks. See: [Prefixes.md](./Prefixes.md)
|
|
190
|
-
* @returns {(ReferenceStruct|null)} An object representing a valid reference or null if it is invalid.
|
|
191
|
-
*/
|
|
192
|
-
export function parseStructRef (ref, options = { xlsx: false }) {
|
|
193
|
-
const r = parseRef(ref, options);
|
|
194
|
-
if (r && r.struct) {
|
|
195
|
-
const structData = parseSRange(r.struct);
|
|
196
|
-
if (structData && structData.length === r.struct.length) {
|
|
197
|
-
return options.xlsx
|
|
198
|
-
? {
|
|
199
|
-
workbookName: r.workbookName,
|
|
200
|
-
sheetName: r.sheetName,
|
|
201
|
-
table: r.name,
|
|
202
|
-
columns: structData.columns,
|
|
203
|
-
sections: structData.sections
|
|
204
|
-
}
|
|
205
|
-
: {
|
|
206
|
-
context: r.context,
|
|
207
|
-
table: r.name,
|
|
208
|
-
columns: structData.columns,
|
|
209
|
-
sections: structData.sections
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
return null;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function quoteColname (str) {
|
|
217
|
-
return str.replace(/([[\]#'@])/g, '\'$1');
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function needsBraces (str) {
|
|
221
|
-
return !/^[a-zA-Z0-9\u00a1-\uffff]+$/.test(str);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function toSentenceCase (str) {
|
|
225
|
-
return str[0].toUpperCase() + str.slice(1).toLowerCase();
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Get a string representation of a structured reference object.
|
|
230
|
-
*
|
|
231
|
-
* ```js
|
|
232
|
-
* stringifyStructRef({
|
|
233
|
-
* context: [ 'workbook.xlsx' ],
|
|
234
|
-
* sections: [ 'data' ],
|
|
235
|
-
* columns: [ 'my column', '@foo' ],
|
|
236
|
-
* table: 'tableName',
|
|
237
|
-
* });
|
|
238
|
-
* // => 'workbook.xlsx!tableName[[#Data],[Column1]:[Column2]]'
|
|
239
|
-
* ```
|
|
240
|
-
*
|
|
241
|
-
* @param {ReferenceStruct} refObject A structured reference object
|
|
242
|
-
* @param {object} [options={}] Options
|
|
243
|
-
* @param {boolean} [options.xlsx=false] Switches to the `[1]Sheet1!A1` or `[1]!name` prefix syntax form for external workbooks. See: [Prefixes.md](./Prefixes.md)
|
|
244
|
-
* @param {boolean} [options.thisRow=false] Enforces using the `[#This Row]` instead of the `@` shorthand when serializing structured ranges.
|
|
245
|
-
* @returns {string} The structured reference in string format
|
|
246
|
-
*/
|
|
247
|
-
export function stringifyStructRef (refObject, options = {}) {
|
|
248
|
-
const { xlsx, thisRow } = options;
|
|
249
|
-
let s = xlsx
|
|
250
|
-
? stringifyPrefixAlt(refObject)
|
|
251
|
-
: stringifyPrefix(refObject);
|
|
252
|
-
|
|
253
|
-
if (refObject.table) {
|
|
254
|
-
s += refObject.table;
|
|
255
|
-
}
|
|
256
|
-
const numColumns = refObject.columns?.length ?? 0;
|
|
257
|
-
const numSections = refObject.sections?.length ?? 0;
|
|
258
|
-
// single section
|
|
259
|
-
if (numSections === 1 && !numColumns) {
|
|
260
|
-
s += `[#${toSentenceCase(refObject.sections[0])}]`;
|
|
261
|
-
}
|
|
262
|
-
// single column
|
|
263
|
-
else if (!numSections && numColumns === 1) {
|
|
264
|
-
s += `[${quoteColname(refObject.columns[0])}]`;
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
s += '[';
|
|
268
|
-
// single [#this row] sections get normalized to an @ by default
|
|
269
|
-
const singleAt = !thisRow && numSections === 1 && refObject.sections[0].toLowerCase() === 'this row';
|
|
270
|
-
if (singleAt) {
|
|
271
|
-
s += '@';
|
|
272
|
-
}
|
|
273
|
-
else if (numSections) {
|
|
274
|
-
s += refObject.sections
|
|
275
|
-
.map(d => `[#${toSentenceCase(d)}]`)
|
|
276
|
-
.join(',');
|
|
277
|
-
if (numColumns) {
|
|
278
|
-
s += ',';
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
// a case of a single alphanumberic column with a [#this row] becomes [@col]
|
|
282
|
-
if (singleAt && refObject.columns.length === 1 && !needsBraces(refObject.columns[0])) {
|
|
283
|
-
s += quoteColname(refObject.columns[0]);
|
|
284
|
-
}
|
|
285
|
-
else if (numColumns) {
|
|
286
|
-
s += refObject.columns.slice(0, 2)
|
|
287
|
-
.map(d => (`[${quoteColname(d)}]`))
|
|
288
|
-
.join(':');
|
|
289
|
-
}
|
|
290
|
-
s += ']';
|
|
291
|
-
}
|
|
292
|
-
return s;
|
|
293
|
-
}
|