@nordcraft/search 1.0.38 → 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.
@@ -4,16 +4,8 @@ import { isToddleFormula } from '@nordcraft/core/dist/formula/formulaTypes';
4
4
  import { ToddleFormula } from '@nordcraft/core/dist/formula/ToddleFormula';
5
5
  import { ToddleApiService } from '@nordcraft/ssr/dist/ToddleApiService';
6
6
  import { ToddleRoute } from '@nordcraft/ssr/dist/ToddleRoute';
7
- import { shouldSearchPath } from './util/helpers';
8
- /**
9
- * Search a project by applying rules to all nodes in the project and returning reported results.
10
- *
11
- * @param files All files to check against
12
- * @param rules All rules to check against
13
- * @param pathsToVisit Only visit specific paths. All subpaths are visited as well. For example, ['components', 'test'] would visit everything under the test component. Defaults is `[]` which means all paths are visited.
14
- * @returns A generator that yields results as they are found
15
- */
16
- export function* searchProject({ files, rules, pathsToVisit = [], state, }) {
7
+ import { shouldSearchExactPath, shouldVisitTree } from './util/helpers';
8
+ export function* searchProject({ files, rules, pathsToVisit = [], useExactPaths = false, state, fixOptions, }) {
17
9
  const memos = new Map();
18
10
  const memo = (key, fn) => {
19
11
  const stringKey = Array.isArray(key) ? key.join('/') : key;
@@ -28,108 +20,163 @@ export function* searchProject({ files, rules, pathsToVisit = [], state, }) {
28
20
  const component = files.components[key];
29
21
  if (component) {
30
22
  yield* visitNode({
31
- nodeType: 'component',
32
- value: component,
33
- path: ['components', key],
34
- rules,
35
- files,
36
- pathsToVisit,
37
- memo,
38
- }, state);
23
+ args: {
24
+ nodeType: 'component',
25
+ value: component,
26
+ path: ['components', key],
27
+ rules,
28
+ files,
29
+ pathsToVisit,
30
+ useExactPaths,
31
+ memo,
32
+ },
33
+ state,
34
+ fixOptions: fixOptions,
35
+ });
39
36
  }
40
37
  }
41
38
  for (const key in files.formulas) {
42
39
  yield* visitNode({
43
- nodeType: 'project-formula',
44
- value: files.formulas[key],
45
- path: ['formulas', key],
46
- rules,
47
- files,
48
- pathsToVisit,
49
- memo,
50
- }, state);
40
+ args: {
41
+ nodeType: 'project-formula',
42
+ value: files.formulas[key],
43
+ path: ['formulas', key],
44
+ rules,
45
+ files,
46
+ pathsToVisit,
47
+ useExactPaths,
48
+ memo,
49
+ },
50
+ state,
51
+ fixOptions: fixOptions,
52
+ });
51
53
  }
52
54
  for (const key in files.actions) {
53
55
  yield* visitNode({
54
- nodeType: 'project-action',
55
- value: files.actions[key],
56
- path: ['actions', key],
57
- rules,
58
- files,
59
- pathsToVisit,
60
- memo,
61
- }, state);
56
+ args: {
57
+ nodeType: 'project-action',
58
+ value: files.actions[key],
59
+ path: ['actions', key],
60
+ rules,
61
+ files,
62
+ pathsToVisit,
63
+ useExactPaths,
64
+ memo,
65
+ },
66
+ state,
67
+ fixOptions: fixOptions,
68
+ });
62
69
  }
63
70
  for (const key in files.themes) {
64
71
  yield* visitNode({
65
- nodeType: 'project-theme',
66
- value: files.themes[key],
67
- path: ['themes', key],
68
- rules,
69
- files,
70
- pathsToVisit,
71
- memo,
72
- }, state);
73
- }
74
- if (files.services) {
75
- for (const key in files.services) {
76
- yield* visitNode({
77
- nodeType: 'api-service',
78
- value: files.services[key],
79
- path: ['services', key],
72
+ args: {
73
+ nodeType: 'project-theme',
74
+ value: files.themes[key],
75
+ path: ['themes', key],
80
76
  rules,
81
77
  files,
82
78
  pathsToVisit,
79
+ useExactPaths,
83
80
  memo,
84
- }, state);
81
+ },
82
+ state,
83
+ fixOptions: fixOptions,
84
+ });
85
+ }
86
+ if (files.services) {
87
+ for (const key in files.services) {
88
+ yield* visitNode({
89
+ args: {
90
+ nodeType: 'api-service',
91
+ value: files.services[key],
92
+ path: ['services', key],
93
+ rules,
94
+ files,
95
+ pathsToVisit,
96
+ useExactPaths,
97
+ memo,
98
+ },
99
+ state,
100
+ fixOptions: fixOptions,
101
+ });
85
102
  }
86
103
  }
87
104
  if (files.routes) {
88
105
  for (const key in files.routes) {
89
106
  yield* visitNode({
90
- nodeType: 'project-route',
91
- value: files.routes[key],
92
- routeName: key,
93
- path: ['routes', key],
94
- rules,
95
- files,
96
- pathsToVisit,
97
- memo,
98
- }, state);
107
+ args: {
108
+ nodeType: 'project-route',
109
+ value: files.routes[key],
110
+ routeName: key,
111
+ path: ['routes', key],
112
+ rules,
113
+ files,
114
+ pathsToVisit,
115
+ useExactPaths,
116
+ memo,
117
+ },
118
+ state,
119
+ fixOptions: fixOptions,
120
+ });
99
121
  }
100
122
  }
101
123
  yield* visitNode({
102
- nodeType: 'project-config',
103
- value: files.config,
104
- path: ['config'],
105
- rules,
106
- files,
107
- pathsToVisit,
108
- memo,
109
- }, state);
124
+ args: {
125
+ nodeType: 'project-config',
126
+ value: files.config,
127
+ path: ['config'],
128
+ rules,
129
+ files,
130
+ pathsToVisit,
131
+ useExactPaths,
132
+ memo,
133
+ },
134
+ state,
135
+ fixOptions: fixOptions,
136
+ });
110
137
  }
111
- function* visitNode(args, state) {
112
- performance.mark(`visitNode-${args.path.join('/')}`);
113
- const { rules, pathsToVisit, ...data } = args;
138
+ function* visitNode({ args, state, fixOptions, }) {
139
+ const { rules, pathsToVisit, useExactPaths, ...data } = args;
114
140
  const { files, value, path, memo, nodeType } = data;
115
- if (!shouldSearchPath(data.path, pathsToVisit)) {
141
+ if (!shouldVisitTree({
142
+ path: data.path,
143
+ pathsToVisit,
144
+ })) {
145
+ // We don't need to search this path or any of its subpaths
116
146
  return;
117
147
  }
118
- const results = [];
119
- for (const rule of rules) {
120
- performance.mark(`rule-${rule.code}`);
121
- rule.visit((path, details) => {
122
- results.push({
123
- code: rule.code,
124
- category: rule.category,
125
- level: rule.level,
126
- path,
127
- details,
128
- });
129
- }, data, state);
130
- }
131
- for (const result of results) {
132
- yield result;
148
+ if (!useExactPaths ||
149
+ shouldSearchExactPath({ path: data.path, pathsToVisit })) {
150
+ if (fixOptions) {
151
+ // We're fixing issues
152
+ for (const rule of rules) {
153
+ const fixedFiles = rule.fixes?.[fixOptions.fixType]?.(data, state);
154
+ if (fixedFiles) {
155
+ yield fixedFiles;
156
+ }
157
+ }
158
+ }
159
+ else {
160
+ // We're looking for issues
161
+ const results = [];
162
+ for (const rule of rules) {
163
+ // eslint-disable-next-line no-console
164
+ console.timeStamp(`Visiting rule ${rule.code}`);
165
+ rule.visit((path, details, fixes) => {
166
+ results.push({
167
+ code: rule.code,
168
+ category: rule.category,
169
+ level: rule.level,
170
+ path,
171
+ details,
172
+ fixes,
173
+ });
174
+ }, data, state);
175
+ }
176
+ for (const result of results) {
177
+ yield result;
178
+ }
179
+ }
133
180
  }
134
181
  switch (nodeType) {
135
182
  case 'component': {
@@ -144,140 +191,195 @@ function* visitNode(args, state) {
144
191
  });
145
192
  for (const key in value.attributes) {
146
193
  yield* visitNode({
147
- nodeType: 'component-attribute',
148
- value: value.attributes[key],
149
- path: [...path, 'attributes', key],
150
- rules,
151
- files,
152
- pathsToVisit,
153
- memo,
154
- component,
155
- }, state);
194
+ args: {
195
+ nodeType: 'component-attribute',
196
+ value: value.attributes[key],
197
+ path: [...path, 'attributes', key],
198
+ rules,
199
+ files,
200
+ pathsToVisit,
201
+ useExactPaths,
202
+ memo,
203
+ component,
204
+ },
205
+ state,
206
+ fixOptions: fixOptions,
207
+ });
156
208
  }
157
209
  for (const key in value.variables) {
158
210
  yield* visitNode({
159
- nodeType: 'component-variable',
160
- value: value.variables[key],
161
- path: [...path, 'variables', key],
162
- rules,
163
- files,
164
- pathsToVisit,
165
- memo,
166
- component,
167
- }, state);
211
+ args: {
212
+ nodeType: 'component-variable',
213
+ value: value.variables[key],
214
+ path: [...path, 'variables', key],
215
+ rules,
216
+ files,
217
+ pathsToVisit,
218
+ useExactPaths,
219
+ memo,
220
+ component,
221
+ },
222
+ state,
223
+ fixOptions: fixOptions,
224
+ });
168
225
  }
169
226
  for (const key in value.apis) {
170
227
  const api = value.apis[key];
171
228
  yield* visitNode({
172
- nodeType: 'component-api',
173
- value: api,
174
- component,
175
- path: [...path, 'apis', key],
176
- rules,
177
- files,
178
- pathsToVisit,
179
- memo,
180
- }, state);
229
+ args: {
230
+ nodeType: 'component-api',
231
+ value: api,
232
+ component,
233
+ path: [...path, 'apis', key],
234
+ rules,
235
+ files,
236
+ pathsToVisit,
237
+ useExactPaths,
238
+ memo,
239
+ },
240
+ state,
241
+ fixOptions: fixOptions,
242
+ });
181
243
  if (!isLegacyApi(api)) {
182
244
  for (const [inputKey, input] of Object.entries(api.inputs)) {
183
245
  yield* visitNode({
184
- nodeType: 'component-api-input',
185
- value: input,
186
- api,
187
- component,
188
- path: [...path, 'apis', key, 'inputs', inputKey],
189
- rules,
190
- files,
191
- pathsToVisit,
192
- memo,
193
- }, state);
246
+ args: {
247
+ nodeType: 'component-api-input',
248
+ value: input,
249
+ api,
250
+ component,
251
+ path: [...path, 'apis', key, 'inputs', inputKey],
252
+ rules,
253
+ files,
254
+ pathsToVisit,
255
+ useExactPaths,
256
+ memo,
257
+ },
258
+ state,
259
+ fixOptions: fixOptions,
260
+ });
194
261
  }
195
262
  }
196
263
  }
197
264
  for (const key in value.formulas) {
198
265
  yield* visitNode({
199
- nodeType: 'component-formula',
200
- value: value.formulas[key],
201
- path: [...path, 'formulas', key],
202
- rules,
203
- files,
204
- pathsToVisit,
205
- memo,
206
- component,
207
- }, state);
266
+ args: {
267
+ nodeType: 'component-formula',
268
+ value: value.formulas[key],
269
+ path: [...path, 'formulas', key],
270
+ rules,
271
+ files,
272
+ pathsToVisit,
273
+ useExactPaths,
274
+ memo,
275
+ component,
276
+ },
277
+ state,
278
+ fixOptions: fixOptions,
279
+ });
208
280
  }
209
281
  for (const key in value.workflows) {
210
282
  yield* visitNode({
211
- nodeType: 'component-workflow',
212
- value: value.workflows[key],
213
- path: [...path, 'workflows', key],
214
- rules,
215
- files,
216
- pathsToVisit,
217
- memo,
218
- component,
219
- }, state);
283
+ args: {
284
+ nodeType: 'component-workflow',
285
+ value: value.workflows[key],
286
+ path: [...path, 'workflows', key],
287
+ rules,
288
+ files,
289
+ pathsToVisit,
290
+ useExactPaths,
291
+ memo,
292
+ component,
293
+ },
294
+ state,
295
+ fixOptions: fixOptions,
296
+ });
220
297
  }
221
298
  for (let i = 0; i < (value.events ?? []).length; i++) {
222
299
  const event = value.events?.[i];
223
300
  if (event) {
224
301
  yield* visitNode({
225
- nodeType: 'component-event',
226
- path: [...path, 'events', i],
227
- rules,
228
- files,
229
- pathsToVisit,
230
- memo,
231
- value: { component, event },
232
- }, state);
302
+ args: {
303
+ nodeType: 'component-event',
304
+ path: [...path, 'events', i],
305
+ rules,
306
+ files,
307
+ pathsToVisit,
308
+ useExactPaths,
309
+ memo,
310
+ value: { component, event },
311
+ },
312
+ state,
313
+ fixOptions: fixOptions,
314
+ });
233
315
  }
234
316
  }
235
317
  for (const key in value.contexts) {
236
318
  yield* visitNode({
237
- nodeType: 'component-context',
238
- value: value.contexts[key],
239
- path: [...path, 'contexts', key],
240
- rules,
241
- files,
242
- pathsToVisit,
243
- memo,
244
- }, state);
319
+ args: {
320
+ nodeType: 'component-context',
321
+ value: value.contexts[key],
322
+ path: [...path, 'contexts', key],
323
+ rules,
324
+ files,
325
+ pathsToVisit,
326
+ useExactPaths,
327
+ memo,
328
+ },
329
+ state,
330
+ fixOptions: fixOptions,
331
+ });
245
332
  }
246
333
  for (const key in value.nodes) {
247
334
  yield* visitNode({
248
- nodeType: 'component-node',
249
- value: value.nodes[key],
250
- path: [...path, 'nodes', key],
251
- rules,
252
- files,
253
- pathsToVisit,
254
- memo,
255
- component,
256
- }, state);
335
+ args: {
336
+ nodeType: 'component-node',
337
+ value: value.nodes[key],
338
+ path: [...path, 'nodes', key],
339
+ rules,
340
+ files,
341
+ pathsToVisit,
342
+ useExactPaths,
343
+ memo,
344
+ component,
345
+ },
346
+ state,
347
+ fixOptions: fixOptions,
348
+ });
257
349
  }
258
350
  for (const { path: formulaPath, formula, } of component.formulasInComponent()) {
259
351
  yield* visitNode({
260
- nodeType: 'formula',
261
- value: formula,
262
- path: [...path, ...formulaPath],
263
- rules,
264
- files,
265
- pathsToVisit,
266
- memo,
267
- component,
268
- }, state);
352
+ args: {
353
+ nodeType: 'formula',
354
+ value: formula,
355
+ path: [...path, ...formulaPath],
356
+ rules,
357
+ files,
358
+ pathsToVisit,
359
+ useExactPaths,
360
+ memo,
361
+ component,
362
+ },
363
+ state,
364
+ fixOptions: fixOptions,
365
+ });
269
366
  }
270
367
  for (const [actionPath, action] of component.actionModelsInComponent()) {
271
368
  yield* visitNode({
272
- nodeType: 'action-model',
273
- value: action,
274
- path: [...path, ...actionPath],
275
- rules,
276
- files,
277
- pathsToVisit,
278
- memo,
279
- component,
280
- }, state);
369
+ args: {
370
+ nodeType: 'action-model',
371
+ value: action,
372
+ path: [...path, ...actionPath],
373
+ rules,
374
+ files,
375
+ pathsToVisit,
376
+ useExactPaths,
377
+ memo,
378
+ component,
379
+ },
380
+ state,
381
+ fixOptions: fixOptions,
382
+ });
281
383
  }
282
384
  break;
283
385
  }
@@ -290,17 +392,21 @@ function* visitNode(args, state) {
290
392
  packages: files.packages,
291
393
  },
292
394
  });
293
- formula.formulasInFormula();
294
395
  for (const { path: formulaPath, formula: f, } of formula.formulasInFormula()) {
295
396
  yield* visitNode({
296
- nodeType: 'formula',
297
- value: f,
298
- path: [...path, ...formulaPath],
299
- rules,
300
- files,
301
- pathsToVisit,
302
- memo,
303
- }, state);
397
+ args: {
398
+ nodeType: 'formula',
399
+ value: f,
400
+ path: [...path, 'formula', ...formulaPath],
401
+ rules,
402
+ files,
403
+ pathsToVisit,
404
+ useExactPaths,
405
+ memo,
406
+ },
407
+ state,
408
+ fixOptions: fixOptions,
409
+ });
304
410
  }
305
411
  }
306
412
  break;
@@ -311,14 +417,19 @@ function* visitNode(args, state) {
311
417
  for (let i = 0; i < variants.length; i++) {
312
418
  const variant = variants[i];
313
419
  yield* visitNode({
314
- nodeType: 'style-variant',
315
- value: { variant, element: value },
316
- path: [...path, 'variants', i],
317
- rules,
318
- files,
319
- pathsToVisit,
320
- memo,
321
- }, state);
420
+ args: {
421
+ nodeType: 'style-variant',
422
+ value: { variant, element: value },
423
+ path: [...path, 'variants', i],
424
+ rules,
425
+ files,
426
+ pathsToVisit,
427
+ useExactPaths,
428
+ memo,
429
+ },
430
+ state,
431
+ fixOptions: fixOptions,
432
+ });
322
433
  }
323
434
  }
324
435
  }
@@ -333,14 +444,19 @@ function* visitNode(args, state) {
333
444
  });
334
445
  for (const { path: formulaPath, formula, } of apiService.formulasInService()) {
335
446
  yield* visitNode({
336
- nodeType: 'formula',
337
- value: formula,
338
- path: [...path, ...formulaPath],
339
- rules,
340
- files,
341
- pathsToVisit,
342
- memo,
343
- }, state);
447
+ args: {
448
+ nodeType: 'formula',
449
+ value: formula,
450
+ path: [...path, ...formulaPath],
451
+ rules,
452
+ files,
453
+ pathsToVisit,
454
+ useExactPaths,
455
+ memo,
456
+ },
457
+ state,
458
+ fixOptions: fixOptions,
459
+ });
344
460
  }
345
461
  break;
346
462
  }
@@ -354,14 +470,19 @@ function* visitNode(args, state) {
354
470
  });
355
471
  for (const { path: formulaPath, formula, } of projectRoute.formulasInRoute()) {
356
472
  yield* visitNode({
357
- nodeType: 'formula',
358
- value: formula,
359
- path: [...path, ...formulaPath],
360
- rules,
361
- files,
362
- pathsToVisit,
363
- memo,
364
- }, state);
473
+ args: {
474
+ nodeType: 'formula',
475
+ value: formula,
476
+ path: [...path, 'formula', ...formulaPath],
477
+ rules,
478
+ files,
479
+ pathsToVisit,
480
+ useExactPaths,
481
+ memo,
482
+ },
483
+ state,
484
+ fixOptions: fixOptions,
485
+ });
365
486
  }
366
487
  break;
367
488
  }