@nordcraft/search 1.0.37 → 1.0.39

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.
@@ -1,8 +1,16 @@
1
- import type { FunctionOperation } from '@nordcraft/core/dist/formula/formula'
1
+ import type {
2
+ AndOperation,
3
+ ArrayOperation,
4
+ FunctionArgument,
5
+ FunctionOperation,
6
+ OrOperation,
7
+ SwitchOperation,
8
+ } from '@nordcraft/core/dist/formula/formula'
2
9
  import { isToddleFormula } from '@nordcraft/core/dist/formula/formulaTypes'
10
+ import { omitKeys, set } from '@nordcraft/core/dist/utils/collections'
3
11
  import { isDefined } from '@nordcraft/core/dist/utils/util'
4
12
  import type { ProjectFiles } from '@nordcraft/ssr/dist/ssr.types'
5
- import type { Rule } from '../../types'
13
+ import type { FormulaNode, NodeType, Rule } from '../../types'
6
14
 
7
15
  export const legacyFormulaRule: Rule<{
8
16
  name: string
@@ -10,18 +18,686 @@ export const legacyFormulaRule: Rule<{
10
18
  code: 'legacy formula',
11
19
  level: 'warning',
12
20
  category: 'Deprecation',
13
- visit: (report, { path, files, value, nodeType }) => {
14
- if (
15
- nodeType !== 'formula' ||
16
- value.type !== 'function' ||
17
- !isLegacyFormula(value, files)
18
- ) {
21
+ visit: (report, data) => {
22
+ if (!isIssue(data)) {
19
23
  return
20
24
  }
21
- report(path, { name: value.name })
25
+ report(
26
+ data.path,
27
+ { name: data.value.name },
28
+ // The TYPE formula cannot be autofixed since the types have changed between the 2 implementations
29
+ data.value.name !== 'TYPE' ? ['replace-legacy-formula'] : undefined,
30
+ )
31
+ },
32
+ fixes: {
33
+ 'replace-legacy-formula': (data) => {
34
+ if (!isIssue(data)) {
35
+ return
36
+ }
37
+ switch (data.value.name) {
38
+ // Known legacy formulas first
39
+ case 'AND': {
40
+ const { name, ...legacyAndFormula } = data.value
41
+ const andFormula: AndOperation = {
42
+ ...legacyAndFormula,
43
+ type: 'and',
44
+ arguments: legacyAndFormula.arguments.map((a) => {
45
+ const { name, ...argument } = a
46
+ return argument
47
+ }),
48
+ }
49
+ return set(data.files, data.path, andFormula)
50
+ }
51
+ case 'CONCAT': {
52
+ const newConcatFormula: FunctionOperation = {
53
+ ...data.value,
54
+ name: '@toddle/concatenate',
55
+ display_name: 'Concatenate',
56
+ }
57
+ return set(data.files, data.path, newConcatFormula)
58
+ }
59
+ case 'DEFAULT': {
60
+ const newDefaultFormula: FunctionOperation = {
61
+ ...data.value,
62
+ name: '@toddle/defaultTo',
63
+ // The old DEFAULT formula did not support variableArguments
64
+ variableArguments: true,
65
+ display_name: 'Default to',
66
+ }
67
+ return set(data.files, data.path, newDefaultFormula)
68
+ }
69
+ case 'DELETE': {
70
+ const newDeleteFormula: FunctionOperation = {
71
+ ...data.value,
72
+ name: '@toddle/deleteKey',
73
+ display_name: 'Delete',
74
+ }
75
+ return set(data.files, data.path, newDeleteFormula)
76
+ }
77
+ case 'DROP_LAST': {
78
+ const newDropLastFormula: FunctionOperation = {
79
+ ...data.value,
80
+ name: '@toddle/dropLast',
81
+ display_name: 'Drop Last',
82
+ arguments: renameArguments(
83
+ ARRAY_ARGUMENT_MAPPINGS,
84
+ data.value.arguments,
85
+ ),
86
+ }
87
+ return set(data.files, data.path, newDropLastFormula)
88
+ }
89
+ case 'EQ': {
90
+ const newEqualsFormula: FunctionOperation = {
91
+ ...data.value,
92
+ name: '@toddle/equals',
93
+ display_name: 'Equals',
94
+ }
95
+ return set(data.files, data.path, newEqualsFormula)
96
+ }
97
+ case 'FIND INDEX': {
98
+ const newFindIndexFormula: FunctionOperation = {
99
+ ...data.value,
100
+ name: '@toddle/findIndex',
101
+ display_name: 'Find index',
102
+ arguments: renameArguments(
103
+ PREDICATE_ARGUMENT_MAPPINGS,
104
+ data.value.arguments,
105
+ ),
106
+ }
107
+ return set(data.files, data.path, newFindIndexFormula)
108
+ }
109
+ case 'FLAT': {
110
+ const newFlattenFormula: FunctionOperation = {
111
+ ...data.value,
112
+ name: '@toddle/flatten',
113
+ display_name: 'Flatten',
114
+ arguments: renameArguments(
115
+ ARRAY_ARGUMENT_MAPPINGS,
116
+ data.value.arguments,
117
+ ),
118
+ }
119
+ return set(data.files, data.path, newFlattenFormula)
120
+ }
121
+ case 'GT': {
122
+ const newGreaterThanFormula: FunctionOperation = {
123
+ ...data.value,
124
+ name: '@toddle/greaterThan',
125
+ display_name: 'Greater than',
126
+ }
127
+ return set(data.files, data.path, newGreaterThanFormula)
128
+ }
129
+ case 'GTE': {
130
+ const newGreaterOrEqualFormula: FunctionOperation = {
131
+ ...data.value,
132
+ name: '@toddle/greaterOrEqueal',
133
+ display_name: 'Greater or equal',
134
+ }
135
+ return set(data.files, data.path, newGreaterOrEqualFormula)
136
+ }
137
+ case 'GROUP_BY': {
138
+ const newGroupbyFormula: FunctionOperation = {
139
+ ...data.value,
140
+ name: '@toddle/groupBy',
141
+ display_name: 'Group by',
142
+ arguments: renameArguments(
143
+ PREDICATE_ARGUMENT_MAPPINGS,
144
+ data.value.arguments,
145
+ ),
146
+ }
147
+ return set(data.files, data.path, newGroupbyFormula)
148
+ }
149
+ case 'IF': {
150
+ const legacyIfFormula = omitKeys(data.value, ['arguments', 'name'])
151
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
152
+ const ifArguments = data.value.arguments ?? []
153
+ const switchFormula: SwitchOperation = {
154
+ ...legacyIfFormula,
155
+ type: 'switch',
156
+ cases: [
157
+ {
158
+ condition: ifArguments[0]?.formula,
159
+ formula: ifArguments[1]?.formula,
160
+ },
161
+ ],
162
+ default: ifArguments[2]?.formula,
163
+ }
164
+ return set(data.files, data.path, switchFormula)
165
+ }
166
+ case 'INDEX OF': {
167
+ const newIndexofFormula: FunctionOperation = {
168
+ ...data.value,
169
+ name: '@toddle/indexOf',
170
+ display_name: 'Index of',
171
+ arguments: renameArguments(
172
+ PREDICATE_ARGUMENT_MAPPINGS,
173
+ data.value.arguments,
174
+ ),
175
+ }
176
+ return set(data.files, data.path, newIndexofFormula)
177
+ }
178
+ case 'JSON_PARSE': {
179
+ const newJsonParseFormula: FunctionOperation = {
180
+ ...data.value,
181
+ name: '@toddle/parseJSON',
182
+ display_name: 'Parse JSON',
183
+ arguments: renameArguments(
184
+ { Input: 'JSON string' },
185
+ data.value.arguments,
186
+ ),
187
+ }
188
+ return set(data.files, data.path, newJsonParseFormula)
189
+ }
190
+ case 'KEY_BY': {
191
+ const newKeyByFormula: FunctionOperation = {
192
+ ...data.value,
193
+ name: '@toddle/keyBy',
194
+ display_name: 'Key by',
195
+ arguments: renameArguments(
196
+ {
197
+ ...ARRAY_ARGUMENT_MAPPINGS,
198
+ 'Key formula': 'Formula',
199
+ },
200
+ data.value.arguments,
201
+ ),
202
+ }
203
+ return set(data.files, data.path, newKeyByFormula)
204
+ }
205
+ case 'LIST': {
206
+ const newArrayFormula: ArrayOperation = {
207
+ type: 'array',
208
+ arguments: data.value.arguments,
209
+ }
210
+ return set(data.files, data.path, newArrayFormula)
211
+ }
212
+ case 'LOWER': {
213
+ const newLowercaseFormula: FunctionOperation = {
214
+ ...data.value,
215
+ name: '@toddle/lowercase',
216
+ display_name: 'Lower case',
217
+ }
218
+ return set(data.files, data.path, newLowercaseFormula)
219
+ }
220
+ case 'LT': {
221
+ const newLessThanFormula: FunctionOperation = {
222
+ ...data.value,
223
+ name: '@toddle/lessThan',
224
+ display_name: 'Less than',
225
+ }
226
+ return set(data.files, data.path, newLessThanFormula)
227
+ }
228
+ case 'LTE': {
229
+ const newLessOrEqualFormula: FunctionOperation = {
230
+ ...data.value,
231
+ name: '@toddle/lessOrEqual',
232
+ display_name: 'Less or equal',
233
+ }
234
+ return set(data.files, data.path, newLessOrEqualFormula)
235
+ }
236
+ case 'MOD': {
237
+ const newModuloFormula: FunctionOperation = {
238
+ ...data.value,
239
+ arguments: renameArguments(
240
+ { Dividor: 'Divider' },
241
+ data.value.arguments,
242
+ ),
243
+ name: '@toddle/modulo',
244
+ display_name: 'Modulo',
245
+ }
246
+ return set(data.files, data.path, newModuloFormula)
247
+ }
248
+ case 'NEQ': {
249
+ const newNotEqualFormula: FunctionOperation = {
250
+ ...data.value,
251
+ name: '@toddle/notEqual',
252
+ display_name: 'Not equal',
253
+ }
254
+ return set(data.files, data.path, newNotEqualFormula)
255
+ }
256
+ case 'OR': {
257
+ const { name, ...legacyOrFormula } = data.value
258
+ // Replace the AND formula with an 'and' formula
259
+ const andFormula: OrOperation = {
260
+ ...legacyOrFormula,
261
+ type: 'or',
262
+ arguments: legacyOrFormula.arguments.map((a) => {
263
+ const { name, ...argument } = a
264
+ return argument
265
+ }),
266
+ }
267
+ return set(data.files, data.path, andFormula)
268
+ }
269
+ case 'RANDOM': {
270
+ const newRandomNumberFormula: FunctionOperation = {
271
+ ...data.value,
272
+ name: '@toddle/randomNumber',
273
+ display_name: 'Random number',
274
+ }
275
+ return set(data.files, data.path, newRandomNumberFormula)
276
+ }
277
+ case 'SIZE': {
278
+ const newSizeFormula: FunctionOperation = {
279
+ ...data.value,
280
+ name: '@toddle/size',
281
+ display_name: 'Size',
282
+ }
283
+ return set(data.files, data.path, newSizeFormula)
284
+ }
285
+ case 'SQRT': {
286
+ const newSqrtFormula: FunctionOperation = {
287
+ ...data.value,
288
+ name: '@toddle/squareRoot',
289
+ display_name: 'Square Root',
290
+ }
291
+ return set(data.files, data.path, newSqrtFormula)
292
+ }
293
+ case 'STARTS_WITH': {
294
+ const newStartsWithFormula: FunctionOperation = {
295
+ ...data.value,
296
+ name: '@toddle/startsWith',
297
+ display_name: 'Starts with',
298
+ arguments: renameArguments(
299
+ { Input: 'String' },
300
+ data.value.arguments,
301
+ ),
302
+ }
303
+ return set(data.files, data.path, newStartsWithFormula)
304
+ }
305
+ case 'TAKE_LAST': {
306
+ const newTakeLastFormula: FunctionOperation = {
307
+ ...data.value,
308
+ name: '@toddle/takeLast',
309
+ display_name: 'Take last',
310
+ arguments: renameArguments(
311
+ ARRAY_ARGUMENT_MAPPINGS,
312
+ data.value.arguments,
313
+ ),
314
+ }
315
+ return set(data.files, data.path, newTakeLastFormula)
316
+ }
317
+ case 'TYPE':
318
+ // We can't autofix this one as the types have changed
319
+ break
320
+ case 'UPPER': {
321
+ const newUpperFormula: FunctionOperation = {
322
+ ...data.value,
323
+ name: '@toddle/uppercase',
324
+ display_name: 'Uppercase',
325
+ arguments: renameArguments(
326
+ ARRAY_ARGUMENT_MAPPINGS,
327
+ data.value.arguments,
328
+ ),
329
+ }
330
+ return set(data.files, data.path, newUpperFormula)
331
+ }
332
+ case 'URI_ENCODE': {
333
+ const newUriEncodeFormula: FunctionOperation = {
334
+ ...data.value,
335
+ name: '@toddle/encodeURIComponent',
336
+ display_name: 'Encode URI Component',
337
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
338
+ arguments: data.value.arguments?.map((arg) => ({
339
+ ...arg,
340
+ // Let's fix this typo as well
341
+ name: arg.name === 'URI' ? 'URIComponent' : arg.name,
342
+ })),
343
+ }
344
+ return set(data.files, data.path, newUriEncodeFormula)
345
+ }
346
+
347
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //
348
+ // ℹ️ Below is handling of the builtin formulas that can be updated ℹ️ //
349
+ // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //
350
+
351
+ case 'ABSOLUTE': {
352
+ const newAbsoluteFormula: FunctionOperation = {
353
+ ...data.value,
354
+ name: '@toddle/absolute',
355
+ display_name: 'Absolute',
356
+ }
357
+ return set(data.files, data.path, newAbsoluteFormula)
358
+ }
359
+ case 'ADD': {
360
+ const newAddFormula: FunctionOperation = {
361
+ ...data.value,
362
+ name: '@toddle/add',
363
+ display_name: 'Add',
364
+ }
365
+ return set(data.files, data.path, newAddFormula)
366
+ }
367
+ case 'APPEND': {
368
+ const newAppendFormula: FunctionOperation = {
369
+ ...data.value,
370
+ name: '@toddle/append',
371
+ display_name: 'Append',
372
+ }
373
+ return set(data.files, data.path, newAppendFormula)
374
+ }
375
+ case 'BOOLEAN': {
376
+ const newBooleanFormula: FunctionOperation = {
377
+ ...data.value,
378
+ name: '@toddle/boolean',
379
+ display_name: 'Boolean',
380
+ }
381
+ return set(data.files, data.path, newBooleanFormula)
382
+ }
383
+ case 'CLAMP': {
384
+ const newClampFormula: FunctionOperation = {
385
+ ...data.value,
386
+ name: '@toddle/clamp',
387
+ display_name: 'Clamp',
388
+ }
389
+ return set(data.files, data.path, newClampFormula)
390
+ }
391
+ case 'DIVIDE': {
392
+ const newDivideFormula: FunctionOperation = {
393
+ ...data.value,
394
+ name: '@toddle/divide',
395
+ display_name: 'Divide',
396
+ }
397
+ return set(data.files, data.path, newDivideFormula)
398
+ }
399
+ case 'DROP': {
400
+ const newDropFormula: FunctionOperation = {
401
+ ...data.value,
402
+ name: '@toddle/drop',
403
+ display_name: 'Drop',
404
+ }
405
+ return set(data.files, data.path, newDropFormula)
406
+ }
407
+ case 'ENTRIES': {
408
+ const newEntriesFormula: FunctionOperation = {
409
+ ...data.value,
410
+ name: '@toddle/entries',
411
+ display_name: 'Entries',
412
+ }
413
+ return set(data.files, data.path, newEntriesFormula)
414
+ }
415
+ case 'EVERY': {
416
+ const newEveryFormula: FunctionOperation = {
417
+ ...data.value,
418
+ name: '@toddle/every',
419
+ display_name: 'Every',
420
+ arguments: renameArguments(
421
+ PREDICATE_ARGUMENT_MAPPINGS,
422
+ data.value.arguments,
423
+ ),
424
+ }
425
+ return set(data.files, data.path, newEveryFormula)
426
+ }
427
+ case 'FILTER': {
428
+ const newFilterFormula: FunctionOperation = {
429
+ ...data.value,
430
+ name: '@toddle/filter',
431
+ display_name: 'Filter',
432
+ arguments: renameArguments(
433
+ PREDICATE_ARGUMENT_MAPPINGS,
434
+ data.value.arguments,
435
+ ),
436
+ }
437
+ return set(data.files, data.path, newFilterFormula)
438
+ }
439
+ case 'FIND': {
440
+ const newFindFormula: FunctionOperation = {
441
+ ...data.value,
442
+ name: '@toddle/find',
443
+ display_name: 'Find',
444
+ arguments: renameArguments(
445
+ PREDICATE_ARGUMENT_MAPPINGS,
446
+ data.value.arguments,
447
+ ),
448
+ }
449
+ return set(data.files, data.path, newFindFormula)
450
+ }
451
+ case 'FROMENTRIES': {
452
+ const newFromentriesFormula: FunctionOperation = {
453
+ ...data.value,
454
+ name: '@toddle/fromEntries',
455
+ display_name: 'From entries',
456
+ arguments: renameArguments(
457
+ PREDICATE_ARGUMENT_MAPPINGS,
458
+ data.value.arguments,
459
+ ),
460
+ }
461
+ return set(data.files, data.path, newFromentriesFormula)
462
+ }
463
+ case 'GET': {
464
+ const newGetFormula: FunctionOperation = {
465
+ ...data.value,
466
+ name: '@toddle/get',
467
+ display_name: 'Get',
468
+ }
469
+ return set(data.files, data.path, newGetFormula)
470
+ }
471
+ case 'INCLUDES': {
472
+ const newIncludesFormula: FunctionOperation = {
473
+ ...data.value,
474
+ name: '@toddle/includes',
475
+ display_name: 'Includes',
476
+ arguments: renameArguments(
477
+ PREDICATE_ARGUMENT_MAPPINGS,
478
+ data.value.arguments,
479
+ ),
480
+ }
481
+ return set(data.files, data.path, newIncludesFormula)
482
+ }
483
+ case 'JOIN': {
484
+ const newJoinFormula: FunctionOperation = {
485
+ ...data.value,
486
+ name: '@toddle/join',
487
+ display_name: 'Join',
488
+ arguments: renameArguments(
489
+ PREDICATE_ARGUMENT_MAPPINGS,
490
+ data.value.arguments,
491
+ ),
492
+ }
493
+ return set(data.files, data.path, newJoinFormula)
494
+ }
495
+ case 'MAP': {
496
+ const newMapFormula: FunctionOperation = {
497
+ ...data.value,
498
+ name: '@toddle/map',
499
+ display_name: 'Map',
500
+ arguments: renameArguments(
501
+ {
502
+ ...ARRAY_ARGUMENT_MAPPINGS,
503
+ 'Mapping fx': 'Formula',
504
+ },
505
+ data.value.arguments,
506
+ ),
507
+ }
508
+ return set(data.files, data.path, newMapFormula)
509
+ }
510
+ case 'MAX': {
511
+ const newMaxFormula: FunctionOperation = {
512
+ ...data.value,
513
+ name: '@toddle/max',
514
+ display_name: 'Max',
515
+ arguments: renameArguments(
516
+ ARRAY_ARGUMENT_MAPPINGS,
517
+ data.value.arguments,
518
+ ),
519
+ }
520
+ return set(data.files, data.path, newMaxFormula)
521
+ }
522
+ case 'MIN': {
523
+ const newMinFormula: FunctionOperation = {
524
+ ...data.value,
525
+ name: '@toddle/min',
526
+ display_name: 'Min',
527
+ arguments: renameArguments(
528
+ ARRAY_ARGUMENT_MAPPINGS,
529
+ data.value.arguments,
530
+ ),
531
+ }
532
+ return set(data.files, data.path, newMinFormula)
533
+ }
534
+ case 'MINUS': {
535
+ const newMinusFormula: FunctionOperation = {
536
+ ...data.value,
537
+ name: '@toddle/minus',
538
+ display_name: 'Minus',
539
+ }
540
+ return set(data.files, data.path, newMinusFormula)
541
+ }
542
+ case 'MULTIPLY': {
543
+ const newMultiplyFormula: FunctionOperation = {
544
+ ...data.value,
545
+ name: '@toddle/multiply',
546
+ display_name: 'Multiply',
547
+ }
548
+ return set(data.files, data.path, newMultiplyFormula)
549
+ }
550
+ case 'NOT': {
551
+ const newNotFormula: FunctionOperation = {
552
+ ...data.value,
553
+ name: '@toddle/not',
554
+ display_name: 'Not',
555
+ }
556
+ return set(data.files, data.path, newNotFormula)
557
+ }
558
+ case 'NUMBER': {
559
+ const newNumberFormula: FunctionOperation = {
560
+ ...data.value,
561
+ name: '@toddle/number',
562
+ display_name: 'Number',
563
+ }
564
+ return set(data.files, data.path, newNumberFormula)
565
+ }
566
+ case 'RANGE': {
567
+ const newRangeFormula: FunctionOperation = {
568
+ ...data.value,
569
+ name: '@toddle/range',
570
+ display_name: 'Range',
571
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
572
+ arguments: data.value.arguments?.map((arg, i) => ({
573
+ ...arg,
574
+ // The Max argument didn't always have a name
575
+ name: i === 1 && typeof arg.name !== 'string' ? 'Max' : arg.name,
576
+ })),
577
+ }
578
+ return set(data.files, data.path, newRangeFormula)
579
+ }
580
+ case 'REDUCE': {
581
+ const newReduceFormula: FunctionOperation = {
582
+ ...data.value,
583
+ name: '@toddle/reduce',
584
+ display_name: 'Reduce',
585
+ arguments: renameArguments(
586
+ { 'Reducer fx': 'Formula' },
587
+ data.value.arguments,
588
+ ),
589
+ }
590
+ return set(data.files, data.path, newReduceFormula)
591
+ }
592
+ case 'REPLACEALL': {
593
+ const newReplaceallFormula: FunctionOperation = {
594
+ ...data.value,
595
+ name: '@toddle/replaceAll',
596
+ display_name: 'Replace all',
597
+ arguments: renameArguments(
598
+ // Yes, there was a typo in the old argument name
599
+ { 'String to repalce': 'Search' },
600
+ data.value.arguments,
601
+ ),
602
+ }
603
+ return set(data.files, data.path, newReplaceallFormula)
604
+ }
605
+ case 'REVERSE': {
606
+ const newReverseFormula: FunctionOperation = {
607
+ ...data.value,
608
+ name: '@toddle/reverse',
609
+ display_name: 'Reverse',
610
+ arguments: renameArguments(
611
+ ARRAY_ARGUMENT_MAPPINGS,
612
+ data.value.arguments,
613
+ ),
614
+ }
615
+ return set(data.files, data.path, newReverseFormula)
616
+ }
617
+ case 'ROUND': {
618
+ const newRoundFormula: FunctionOperation = {
619
+ ...data.value,
620
+ name: '@toddle/round',
621
+ display_name: 'Round',
622
+ }
623
+ return set(data.files, data.path, newRoundFormula)
624
+ }
625
+ case 'SET': {
626
+ const newSetFormula: FunctionOperation = {
627
+ ...data.value,
628
+ name: '@toddle/set',
629
+ display_name: 'Set',
630
+ }
631
+ return set(data.files, data.path, newSetFormula)
632
+ }
633
+ case 'SOME': {
634
+ const newSomeFormula: FunctionOperation = {
635
+ ...data.value,
636
+ name: '@toddle/some',
637
+ display_name: 'Some',
638
+ arguments: renameArguments(
639
+ PREDICATE_ARGUMENT_MAPPINGS,
640
+ data.value.arguments,
641
+ ),
642
+ }
643
+ return set(data.files, data.path, newSomeFormula)
644
+ }
645
+ case 'SPLIT': {
646
+ const newSplitFormula: FunctionOperation = {
647
+ ...data.value,
648
+ name: '@toddle/split',
649
+ display_name: 'Split',
650
+ }
651
+ return set(data.files, data.path, newSplitFormula)
652
+ }
653
+ case 'STRING': {
654
+ const newStringFormula: FunctionOperation = {
655
+ ...data.value,
656
+ name: '@toddle/string',
657
+ display_name: 'String',
658
+ }
659
+ return set(data.files, data.path, newStringFormula)
660
+ }
661
+ case 'SUM': {
662
+ const newSumFormula: FunctionOperation = {
663
+ ...data.value,
664
+ name: '@toddle/sum',
665
+ display_name: 'Sum',
666
+ }
667
+ return set(data.files, data.path, newSumFormula)
668
+ }
669
+ case 'TAKE': {
670
+ const newTakeFormula: FunctionOperation = {
671
+ ...data.value,
672
+ name: '@toddle/take',
673
+ display_name: 'Take',
674
+ arguments: renameArguments(
675
+ ARRAY_ARGUMENT_MAPPINGS,
676
+ data.value.arguments,
677
+ ),
678
+ }
679
+ return set(data.files, data.path, newTakeFormula)
680
+ }
681
+ case 'TRIM': {
682
+ const newTrimFormula: FunctionOperation = {
683
+ ...data.value,
684
+ name: '@toddle/trim',
685
+ display_name: 'Trim',
686
+ }
687
+ return set(data.files, data.path, newTrimFormula)
688
+ }
689
+ }
690
+ },
22
691
  },
23
692
  }
24
693
 
694
+ const isIssue = (data: NodeType): data is FormulaNode<FunctionOperation> =>
695
+ data.nodeType === 'formula' &&
696
+ data.value.type === 'function' &&
697
+ isLegacyFormula(data.value, data.files)
698
+
699
+ export type LegacyFormulaRuleFix = 'replace-legacy-formula'
700
+
25
701
  const isLegacyFormula = (
26
702
  formula: FunctionOperation,
27
703
  files: Omit<ProjectFiles, 'config'> & Partial<Pick<ProjectFiles, 'config'>>,
@@ -47,21 +723,34 @@ const isUpperCase = (str: string) => str === str.toUpperCase()
47
723
  const legacyFormulas = new Set([
48
724
  'and',
49
725
  'concat',
50
- 'count',
51
726
  'default',
52
727
  'delete',
728
+ 'drop_last',
53
729
  'eq',
730
+ 'find index',
54
731
  'flat',
55
732
  'gt',
56
733
  'gte',
734
+ 'group_by',
57
735
  'if',
736
+ 'index of',
737
+ 'json_parse',
738
+ 'key_by',
58
739
  'list',
740
+ 'lower',
59
741
  'lt',
60
742
  'lte',
61
743
  'mod',
62
744
  'neq',
63
745
  'or',
746
+ 'random',
747
+ 'size',
748
+ 'sqrt',
749
+ 'starts_with',
750
+ 'take_last',
64
751
  'type',
752
+ 'upper',
753
+ 'uri_encode',
65
754
  ])
66
755
 
67
756
  // cSpell: disable
@@ -156,3 +845,23 @@ const builtInFormulas = new Set([
156
845
  'uppercase',
157
846
  ])
158
847
  // cSpell: enable
848
+
849
+ const ARRAY_ARGUMENT_MAPPINGS = { List: 'Array' }
850
+ const PREDICATE_ARGUMENT_MAPPINGS = {
851
+ ...ARRAY_ARGUMENT_MAPPINGS,
852
+ 'Predicate fx': 'Formula',
853
+ }
854
+
855
+ const renameArguments = (
856
+ mappings: Record<string, string>,
857
+ args: FunctionArgument[] | undefined,
858
+ ): FunctionArgument[] =>
859
+ args?.map((arg) => ({
860
+ ...arg,
861
+ // Let's adjust the names
862
+ name:
863
+ typeof arg.name === 'string'
864
+ ? // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
865
+ (mappings[arg.name] ?? arg.name)
866
+ : arg.name,
867
+ })) ?? []