@nordcraft/search 1.0.88 → 1.0.90

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 (52) hide show
  1. package/dist/rules/issues/actions/noReferenceProjectActionRule.js +2 -27
  2. package/dist/rules/issues/actions/noReferenceProjectActionRule.js.map +1 -1
  3. package/dist/rules/issues/actions/projectActionIsReferenced.memo.d.ts +3 -0
  4. package/dist/rules/issues/actions/projectActionIsReferenced.memo.js +38 -0
  5. package/dist/rules/issues/actions/projectActionIsReferenced.memo.js.map +1 -0
  6. package/dist/rules/issues/attributes/noReferenceAttributeInInstanceRule.js +2 -2
  7. package/dist/rules/issues/attributes/noReferenceAttributeInInstanceRule.js.map +1 -1
  8. package/dist/rules/issues/components/componentIsReferenced.memo.d.ts +3 -0
  9. package/dist/rules/issues/components/componentIsReferenced.memo.js +25 -0
  10. package/dist/rules/issues/components/componentIsReferenced.memo.js.map +1 -0
  11. package/dist/rules/issues/components/noReferenceComponentRule.d.ts +1 -3
  12. package/dist/rules/issues/components/noReferenceComponentRule.js +8 -26
  13. package/dist/rules/issues/components/noReferenceComponentRule.js.map +1 -1
  14. package/dist/rules/issues/dom/nonEmptyVoidElementRule.js +1 -1
  15. package/dist/rules/issues/dom/nonEmptyVoidElementRule.js.map +1 -1
  16. package/dist/rules/issues/formulas/noReferenceProjectFormulaRule.js +2 -85
  17. package/dist/rules/issues/formulas/noReferenceProjectFormulaRule.js.map +1 -1
  18. package/dist/rules/issues/formulas/projectFormulaIsReferenced.memo.d.ts +3 -0
  19. package/dist/rules/issues/formulas/projectFormulaIsReferenced.memo.js +84 -0
  20. package/dist/rules/issues/formulas/projectFormulaIsReferenced.memo.js.map +1 -0
  21. package/dist/rules/issues/miscellaneous/createStaticSizeConstraintRule.js +1 -1
  22. package/dist/rules/issues/miscellaneous/createStaticSizeConstraintRule.js.map +1 -1
  23. package/dist/rules/issues/miscellaneous/miscRules.index.js +2 -0
  24. package/dist/rules/issues/miscellaneous/miscRules.index.js.map +1 -1
  25. package/dist/rules/issues/miscellaneous/noReferencePackageRule.d.ts +5 -0
  26. package/dist/rules/issues/miscellaneous/noReferencePackageRule.js +54 -0
  27. package/dist/rules/issues/miscellaneous/noReferencePackageRule.js.map +1 -0
  28. package/dist/rules/issues/style/styleRules.index.d.ts +3 -1
  29. package/dist/rules/issues/style/unknownCSSVariable.d.ts +3 -2
  30. package/dist/rules/issues/style/unknownCSSVariable.js +79 -36
  31. package/dist/rules/issues/style/unknownCSSVariable.js.map +1 -1
  32. package/dist/searchProject.js +23 -0
  33. package/dist/searchProject.js.map +1 -1
  34. package/dist/types.d.ts +11 -4
  35. package/package.json +3 -3
  36. package/src/rules/issues/actions/noReferenceProjectActionRule.ts +2 -32
  37. package/src/rules/issues/actions/projectActionIsReferenced.memo.ts +52 -0
  38. package/src/rules/issues/attributes/noReferenceAttributeInInstanceRule.ts +2 -2
  39. package/src/rules/issues/components/componentIsReferenced.memo.ts +39 -0
  40. package/src/rules/issues/components/noReferenceComponentRule.ts +10 -39
  41. package/src/rules/issues/dom/nonEmptyVoidElementRule.ts +1 -1
  42. package/src/rules/issues/formulas/noReferenceProjectFormulaRule.test.ts +33 -0
  43. package/src/rules/issues/formulas/noReferenceProjectFormulaRule.ts +2 -102
  44. package/src/rules/issues/formulas/projectFormulaIsReferenced.memo.ts +112 -0
  45. package/src/rules/issues/miscellaneous/createStaticSizeConstraintRule.ts +1 -1
  46. package/src/rules/issues/miscellaneous/miscRules.index.ts +2 -0
  47. package/src/rules/issues/miscellaneous/noReferencePackageRule.test.ts +289 -0
  48. package/src/rules/issues/miscellaneous/noReferencePackageRule.ts +72 -0
  49. package/src/rules/issues/style/unknownCSSVariable.test.ts +107 -44
  50. package/src/rules/issues/style/unknownCSSVariable.ts +101 -46
  51. package/src/searchProject.ts +24 -0
  52. package/src/types.ts +13 -0
@@ -290,6 +290,39 @@ describe('find noReferenceFormulaRule', () => {
290
290
 
291
291
  expect(problems).toBeEmpty()
292
292
  })
293
+
294
+ test('should report if formula is only used by itself', () => {
295
+ const problems = Array.from(
296
+ searchProject({
297
+ files: {
298
+ formulas: {
299
+ 'self-referencing-formula': {
300
+ name: 'self-referencing-formula',
301
+ arguments: [],
302
+ formula: {
303
+ type: 'and',
304
+ arguments: [
305
+ {
306
+ formula: {
307
+ type: 'function',
308
+ name: 'self-referencing-formula',
309
+ arguments: [],
310
+ },
311
+ },
312
+ ],
313
+ },
314
+ },
315
+ },
316
+ components: {},
317
+ },
318
+ rules: [noReferenceProjectFormulaRule],
319
+ }),
320
+ )
321
+
322
+ expect(problems).toHaveLength(1)
323
+ expect(problems[0].code).toBe('no-reference project formula')
324
+ expect(problems[0].path).toEqual(['formulas', 'self-referencing-formula'])
325
+ })
293
326
  })
294
327
 
295
328
  describe('fix noReferenceFormulaRule', () => {
@@ -1,10 +1,6 @@
1
- import { ToddleComponent } from '@nordcraft/core/dist/component/ToddleComponent'
2
- import type { Formula } from '@nordcraft/core/dist/formula/formula'
3
- import { isToddleFormula } from '@nordcraft/core/dist/formula/formula'
4
- import { ToddleApiService } from '@nordcraft/ssr/dist/ToddleApiService'
5
- import { ToddleRoute } from '@nordcraft/ssr/dist/ToddleRoute'
6
1
  import type { Rule } from '../../../types'
7
2
  import { removeFromPathFix } from '../../../util/removeUnused.fix'
3
+ import { projectFormulaIsReferenced } from './projectFormulaIsReferenced.memo'
8
4
 
9
5
  export const noReferenceProjectFormulaRule: Rule<void> = {
10
6
  code: 'no-reference project formula',
@@ -15,76 +11,10 @@ export const noReferenceProjectFormulaRule: Rule<void> = {
15
11
  return
16
12
  }
17
13
 
18
- // Check in all API services first, since that should be quick
19
- for (const apiService of Object.values(files.services ?? {})) {
20
- const service = new ToddleApiService({
21
- service: apiService,
22
- globalFormulas: { formulas: files.formulas, packages: files.packages },
23
- })
24
- const formulas = service.formulasInService()
25
- for (const { path: _formulaPath, formula } of formulas) {
26
- // Check if the formula is used in the formula
27
- if (checkFormula(formula, value.name)) {
28
- return
29
- }
30
- }
31
- }
32
-
33
- // Check routes before components, since they should be quicker
34
- for (const projectRoute of Object.values(files.routes ?? {})) {
35
- const route = new ToddleRoute({
36
- route: projectRoute,
37
- globalFormulas: { formulas: files.formulas, packages: files.packages },
38
- })
39
- const formulas = route.formulasInRoute()
40
- for (const { path: _formulaPath, formula } of formulas) {
41
- // Check if the formula is used in the formula
42
- if (checkFormula(formula, value.name)) {
43
- return
44
- }
45
- }
46
- }
47
-
48
- const componentFormulaReferences = memo(
49
- 'componentFormulaReferences',
50
- () => {
51
- const usedFormulas = new Set<string>()
52
- for (const component of Object.values(files.components)) {
53
- const c = new ToddleComponent({
54
- // Enforce that the component is not undefined since we're iterating
55
- component: component!,
56
- getComponent: (name) => files.components[name],
57
- packageName: undefined,
58
- globalFormulas: {
59
- formulas: files.formulas,
60
- packages: files.packages,
61
- },
62
- })
63
- for (const { formula } of c.formulasInComponent()) {
64
- if (formula.type === 'function') {
65
- usedFormulas.add(formula.name)
66
- }
67
- }
68
- }
69
- return usedFormulas
70
- },
71
- )
72
-
73
- if (componentFormulaReferences.has(value.name)) {
14
+ if (projectFormulaIsReferenced(files, memo)(value.name)) {
74
15
  return
75
16
  }
76
17
 
77
- // TODO: Memoize similar to above. We need have a helper class `ToddleFormula` with `ToddleFormula.normalizeFormulas()`
78
- for (const f of Object.values(files.formulas ?? {})) {
79
- if (f.name === value.name) {
80
- continue
81
- }
82
-
83
- // Check if the formula is used in the formula
84
- if (isToddleFormula(f) && checkFormula(f.formula, value.name)) {
85
- return
86
- }
87
- }
88
18
  report({
89
19
  path,
90
20
  info: {
@@ -100,33 +30,3 @@ export const noReferenceProjectFormulaRule: Rule<void> = {
100
30
  }
101
31
 
102
32
  export type NoReferenceProjectFormulaRuleFix = 'delete-project-formula'
103
-
104
- const checkArguments = (
105
- args: {
106
- formula: Formula
107
- }[],
108
- formulaName: string,
109
- ) => {
110
- args.forEach((a) => {
111
- if (checkFormula(a.formula, formulaName)) {
112
- return true
113
- }
114
- })
115
- return false
116
- }
117
-
118
- const checkFormula = (formula: Formula, formulaName: string) => {
119
- if (formula.type === 'function' && formula.name === formulaName) {
120
- return true
121
- }
122
- if (
123
- formula.type === 'object' ||
124
- formula.type === 'array' ||
125
- formula.type === 'or' ||
126
- formula.type === 'and' ||
127
- formula.type === 'apply'
128
- ) {
129
- checkArguments(formula.arguments ?? [], formulaName)
130
- }
131
- return false
132
- }
@@ -0,0 +1,112 @@
1
+ import { ToddleComponent } from '@nordcraft/core/dist/component/ToddleComponent'
2
+ import {
3
+ isToddleFormula,
4
+ type Formula,
5
+ } from '@nordcraft/core/dist/formula/formula'
6
+ import { isDefined } from '@nordcraft/core/dist/utils/util'
7
+ import type { ProjectFiles } from '@nordcraft/ssr/dist/ssr.types'
8
+ import { ToddleApiService } from '@nordcraft/ssr/dist/ToddleApiService'
9
+ import { ToddleRoute } from '@nordcraft/ssr/dist/ToddleRoute'
10
+ import type { MemoFn } from '../../../types'
11
+
12
+ export const projectFormulaIsReferenced = (
13
+ files: Omit<ProjectFiles, 'config'> & Partial<Pick<ProjectFiles, 'config'>>,
14
+ memo: MemoFn,
15
+ ) => {
16
+ const allUsedFormulaKeys = memo('all-used-formulas', () => {
17
+ const usedFormulas = new Set<string>()
18
+
19
+ // Check in all API services first, since that should be quick
20
+ for (const apiService of Object.values(files.services ?? {})) {
21
+ const service = new ToddleApiService({
22
+ service: apiService,
23
+ globalFormulas: { formulas: files.formulas, packages: files.packages },
24
+ })
25
+ const formulas = service.formulasInService()
26
+ for (const { path: _formulaPath, formula } of formulas) {
27
+ // Check if the formula is used in the formula
28
+ checkFormula(usedFormulas, formula)
29
+ }
30
+ }
31
+
32
+ // Check routes before components, since they should be quicker
33
+ for (const projectRoute of Object.values(files.routes ?? {})) {
34
+ const route = new ToddleRoute({
35
+ route: projectRoute,
36
+ globalFormulas: { formulas: files.formulas, packages: files.packages },
37
+ })
38
+ const formulas = route.formulasInRoute()
39
+ for (const { path: _formulaPath, formula } of formulas) {
40
+ // Check if the formula is used in the formula
41
+ checkFormula(usedFormulas, formula)
42
+ }
43
+ }
44
+
45
+ for (const component of Object.values(files.components)) {
46
+ const c = new ToddleComponent({
47
+ // Enforce that the component is not undefined since we're iterating
48
+ component: component!,
49
+ getComponent: (name) => files.components[name],
50
+ packageName: undefined,
51
+ globalFormulas: {
52
+ formulas: files.formulas,
53
+ packages: files.packages,
54
+ },
55
+ })
56
+ for (const { formula } of c.formulasInComponent()) {
57
+ if (formula.type === 'function') {
58
+ usedFormulas.add(
59
+ [formula.package, formula.name].filter(isDefined).join('/'),
60
+ )
61
+ }
62
+ }
63
+ }
64
+
65
+ for (const f of Object.values(files.formulas ?? {})) {
66
+ // Check if the formula is used in the formula
67
+ if (isToddleFormula(f)) {
68
+ const usedFormulasMinusSelf = new Set<string>()
69
+ checkFormula(usedFormulasMinusSelf, f.formula)
70
+ // Add all minus self (a formula is unused, if it's only used by itself)
71
+ usedFormulasMinusSelf.delete(f.name)
72
+ usedFormulasMinusSelf.forEach((f) => usedFormulas.add(f))
73
+ }
74
+ }
75
+
76
+ return usedFormulas
77
+ })
78
+
79
+ return (formulaName: string, packageName?: string) => {
80
+ return allUsedFormulaKeys.has(
81
+ [packageName, formulaName].filter(isDefined).join('/'),
82
+ )
83
+ }
84
+ }
85
+
86
+ const checkArguments = (
87
+ usedFormulas: Set<string>,
88
+ args: {
89
+ formula: Formula
90
+ }[],
91
+ ) => {
92
+ args.forEach((a) => {
93
+ checkFormula(usedFormulas, a.formula)
94
+ })
95
+ }
96
+
97
+ const checkFormula = (usedFormulas: Set<string>, formula: Formula) => {
98
+ if (formula.type === 'function') {
99
+ usedFormulas.add(
100
+ [formula.package, formula.name].filter(isDefined).join('/'),
101
+ )
102
+ checkArguments(usedFormulas, formula.arguments ?? [])
103
+ } else if (
104
+ formula.type === 'object' ||
105
+ formula.type === 'array' ||
106
+ formula.type === 'or' ||
107
+ formula.type === 'and' ||
108
+ formula.type === 'apply'
109
+ ) {
110
+ checkArguments(usedFormulas, formula.arguments ?? [])
111
+ }
112
+ }
@@ -1,6 +1,6 @@
1
1
  import type { NodeModel } from '@nordcraft/core/dist/component/component.types'
2
+ import { VOID_HTML_ELEMENTS } from '@nordcraft/core/dist/utils/html'
2
3
  import { isDefined } from '@nordcraft/core/dist/utils/util'
3
- import { VOID_HTML_ELEMENTS } from '@nordcraft/ssr/dist/const'
4
4
  import type { Level, Rule } from '../../../types'
5
5
 
6
6
  export function createStaticSizeConstraintRule(
@@ -1,5 +1,6 @@
1
1
  import { createStaticSizeConstraintRule } from './createStaticSizeConstraintRule'
2
2
  import { noReferenceNodeRule } from './noReferenceNodeRule'
3
+ import { noReferenceProjectPackageRule } from './noReferencePackageRule'
3
4
  import { requireExtensionRule } from './requireExtensionRule'
4
5
  import { unknownCookieRule } from './unknownCookieRule'
5
6
 
@@ -11,4 +12,5 @@ export default [
11
12
  createStaticSizeConstraintRule('svg', 100 * 1024),
12
13
  // 50 KB is a large img element (with potential base64 encoded image)
13
14
  createStaticSizeConstraintRule('img', 50 * 1024),
15
+ noReferenceProjectPackageRule,
14
16
  ]
@@ -0,0 +1,289 @@
1
+ import type { ProjectFiles } from '@nordcraft/ssr/src/ssr.types'
2
+ import { describe, expect, test } from 'bun:test'
3
+ import { fixProject } from '../../../fixProject'
4
+ import { searchProject } from '../../../searchProject'
5
+ import { noReferenceProjectPackageRule } from './noReferencePackageRule'
6
+
7
+ describe('noReferenceProjectPackageRule', () => {
8
+ test('should report when no formula, actions or components from a package is used', () => {
9
+ const problems = Array.from(
10
+ searchProject({
11
+ files: {
12
+ formulas: {},
13
+ components: {},
14
+ packages: {
15
+ 'my-pkg': {
16
+ manifest: { name: 'my-pkg', commit: 'abc' },
17
+ formulas: {
18
+ f1: {
19
+ name: 'f1',
20
+ exported: true,
21
+ arguments: [],
22
+ formula: { type: 'value', value: 1 },
23
+ },
24
+ },
25
+ actions: {
26
+ a1: {
27
+ name: 'a1',
28
+ version: 2,
29
+ exported: true,
30
+ handler: () => {},
31
+ variableArguments: false,
32
+ },
33
+ },
34
+ components: {
35
+ c1: {
36
+ name: 'c1',
37
+ exported: true,
38
+ nodes: {
39
+ root: {
40
+ type: 'element',
41
+ tag: 'div',
42
+ children: [],
43
+ attrs: {},
44
+ style: {},
45
+ events: {},
46
+ classes: {},
47
+ },
48
+ },
49
+ },
50
+ },
51
+ },
52
+ },
53
+ },
54
+ rules: [noReferenceProjectPackageRule],
55
+ }),
56
+ )
57
+
58
+ expect(problems).toHaveLength(1)
59
+ expect(problems[0].path).toEqual(['packages', 'my-pkg'])
60
+ })
61
+
62
+ test('should not report if just one formula is used', () => {
63
+ const problems = Array.from(
64
+ searchProject({
65
+ files: {
66
+ formulas: {},
67
+ components: {
68
+ comp: {
69
+ name: 'comp',
70
+ nodes: {
71
+ root: {
72
+ type: 'element',
73
+ tag: 'div',
74
+ children: [],
75
+ attrs: {},
76
+ style: {},
77
+ events: {
78
+ onClick: {
79
+ trigger: 'click',
80
+ actions: [
81
+ {
82
+ type: 'SetVariable',
83
+ variable: 'v1',
84
+ data: {
85
+ type: 'function',
86
+ name: 'f1',
87
+ package: 'my-pkg',
88
+ arguments: [],
89
+ },
90
+ },
91
+ ],
92
+ },
93
+ },
94
+ classes: {},
95
+ },
96
+ },
97
+ },
98
+ },
99
+ packages: {
100
+ 'my-pkg': {
101
+ manifest: { name: 'my-pkg', commit: 'abc' },
102
+ formulas: {
103
+ f1: {
104
+ name: 'f1',
105
+ exported: true,
106
+ arguments: [],
107
+ formula: { type: 'value', value: 1 },
108
+ },
109
+ },
110
+ actions: {},
111
+ components: {},
112
+ },
113
+ },
114
+ },
115
+ rules: [noReferenceProjectPackageRule],
116
+ }),
117
+ )
118
+
119
+ expect(problems).toBeEmpty()
120
+ })
121
+
122
+ test('should not report if just one action is used', () => {
123
+ const problems = Array.from(
124
+ searchProject({
125
+ files: {
126
+ formulas: {},
127
+ components: {
128
+ comp: {
129
+ name: 'comp',
130
+ nodes: {
131
+ root: {
132
+ type: 'element',
133
+ tag: 'button',
134
+ children: [],
135
+ attrs: {},
136
+ style: {},
137
+ events: {
138
+ click: {
139
+ trigger: 'click',
140
+ actions: [
141
+ {
142
+ type: 'Custom',
143
+ name: 'a1',
144
+ package: 'my-pkg',
145
+ arguments: [],
146
+ },
147
+ ],
148
+ },
149
+ },
150
+ classes: {},
151
+ },
152
+ },
153
+ },
154
+ },
155
+ packages: {
156
+ 'my-pkg': {
157
+ manifest: { name: 'my-pkg', commit: 'abc' },
158
+ formulas: {},
159
+ actions: {
160
+ a1: {
161
+ name: 'a1',
162
+ version: 2,
163
+ exported: true,
164
+ handler: () => {},
165
+ variableArguments: false,
166
+ },
167
+ },
168
+ components: {},
169
+ },
170
+ },
171
+ },
172
+ rules: [noReferenceProjectPackageRule],
173
+ }),
174
+ )
175
+
176
+ expect(problems).toHaveLength(0)
177
+ })
178
+
179
+ test('should not report if just one component is used', () => {
180
+ const problems = Array.from(
181
+ searchProject({
182
+ files: {
183
+ formulas: {},
184
+ components: {
185
+ main: {
186
+ name: 'main',
187
+ nodes: {
188
+ root: {
189
+ type: 'component',
190
+ name: 'c1',
191
+ package: 'my-pkg',
192
+ attrs: {},
193
+ children: [],
194
+ events: {},
195
+ },
196
+ },
197
+ },
198
+ },
199
+ packages: {
200
+ 'my-pkg': {
201
+ manifest: { name: 'my-pkg', commit: 'abc' },
202
+ formulas: {},
203
+ actions: {},
204
+ components: {
205
+ c1: {
206
+ name: 'c1',
207
+ exported: true,
208
+ nodes: {
209
+ root: {
210
+ type: 'element',
211
+ tag: 'div',
212
+ children: [],
213
+ attrs: {},
214
+ style: {},
215
+ events: {},
216
+ classes: {},
217
+ },
218
+ },
219
+ },
220
+ },
221
+ },
222
+ },
223
+ },
224
+ rules: [noReferenceProjectPackageRule],
225
+ }),
226
+ )
227
+
228
+ expect(problems).toHaveLength(0)
229
+ })
230
+
231
+ test('should report when a package has no components, actions or formulas', () => {
232
+ const problems = Array.from(
233
+ searchProject({
234
+ files: {
235
+ formulas: {},
236
+ components: {},
237
+ packages: {
238
+ 'empty-pkg': {
239
+ manifest: { name: 'empty-pkg', commit: 'abc' },
240
+ formulas: {},
241
+ actions: {},
242
+ components: {},
243
+ },
244
+ },
245
+ },
246
+ rules: [noReferenceProjectPackageRule],
247
+ }),
248
+ )
249
+
250
+ expect(problems).toHaveLength(1)
251
+ expect(problems[0].path).toEqual(['packages', 'empty-pkg'])
252
+ })
253
+ })
254
+
255
+ describe('fix noReferenceProjectPackageRule', () => {
256
+ test('should remove unreferenced package', () => {
257
+ const project: ProjectFiles = {
258
+ formulas: {},
259
+ components: {},
260
+ packages: {
261
+ 'my-pkg': {
262
+ manifest: { name: 'my-pkg', commit: 'abc' },
263
+ formulas: {},
264
+ actions: {},
265
+ components: {},
266
+ },
267
+ },
268
+ }
269
+
270
+ const problems = Array.from(
271
+ searchProject({
272
+ files: project,
273
+ rules: [noReferenceProjectPackageRule],
274
+ }),
275
+ )
276
+
277
+ expect(problems).toHaveLength(1)
278
+ expect(problems[0].code).toBe('no-reference project package')
279
+ expect(problems[0].fixes).toEqual(['uninstall-package'])
280
+
281
+ const fixedProject = fixProject({
282
+ files: project,
283
+ rule: noReferenceProjectPackageRule,
284
+ fixType: 'uninstall-package',
285
+ })
286
+
287
+ expect(fixedProject.packages).toEqual({})
288
+ })
289
+ })
@@ -0,0 +1,72 @@
1
+ import type { PluginActionV2 } from '@nordcraft/core/dist/types'
2
+ import type { Rule } from '../../../types'
3
+ import { removeFromPathFix } from '../../../util/removeUnused.fix'
4
+ import { projectActionIsReferenced } from '../actions/projectActionIsReferenced.memo'
5
+ import { componentIsReferenced } from '../components/componentIsReferenced.memo'
6
+ import { projectFormulaIsReferenced } from '../formulas/projectFormulaIsReferenced.memo'
7
+
8
+ export const noReferenceProjectPackageRule: Rule<{ node: string }> = {
9
+ code: 'no-reference project package',
10
+ level: 'info',
11
+ category: 'No References',
12
+ visit: (report, info) => {
13
+ if (info.nodeType !== 'project-package') {
14
+ return
15
+ }
16
+
17
+ const { files, memo, path, value, packageName } = info
18
+
19
+ const exportedFormulas = Object.entries(value.formulas ?? {}).filter(
20
+ ([, formula]) => formula.exported,
21
+ )
22
+ if (exportedFormulas.length > 0) {
23
+ const projectFormulaIsReferencedFn = projectFormulaIsReferenced(
24
+ files,
25
+ memo,
26
+ )
27
+ for (const [, formula] of exportedFormulas) {
28
+ if (projectFormulaIsReferencedFn(formula.name, packageName)) {
29
+ return
30
+ }
31
+ }
32
+ }
33
+
34
+ const exportedActions = Object.entries(value.actions ?? {}).filter(
35
+ ([, action]) => (action as PluginActionV2).exported,
36
+ )
37
+ if (exportedActions.length > 0) {
38
+ const projectActionIsReferencedFn = projectActionIsReferenced(files, memo)
39
+ for (const [, action] of exportedActions) {
40
+ if (projectActionIsReferencedFn(action.name, packageName)) {
41
+ return
42
+ }
43
+ }
44
+ }
45
+
46
+ const exportedComponents = Object.entries(value.components ?? {}).filter(
47
+ ([, component]) => component?.exported,
48
+ )
49
+ if (exportedComponents.length > 0) {
50
+ const componentIsReferencedFn = componentIsReferenced(files, memo)
51
+ for (const [, component] of exportedComponents) {
52
+ if (component && componentIsReferencedFn(component.name, packageName)) {
53
+ return
54
+ }
55
+ }
56
+ }
57
+
58
+ report({
59
+ path,
60
+ info: {
61
+ title: `Unused package`,
62
+ description: `Package is installed, but none of its formulas, actions, or components are used by any other part of the project. The package can safely be uninstalled.`,
63
+ },
64
+ fixes: ['uninstall-package'],
65
+ })
66
+ },
67
+ fixes: {
68
+ 'uninstall-package': removeFromPathFix,
69
+ },
70
+ }
71
+
72
+ export type NoReferenceProjectPackageRuleFix = 'uninstall-package'