@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.
- package/dist/rules/issues/actions/noReferenceProjectActionRule.js +2 -27
- package/dist/rules/issues/actions/noReferenceProjectActionRule.js.map +1 -1
- package/dist/rules/issues/actions/projectActionIsReferenced.memo.d.ts +3 -0
- package/dist/rules/issues/actions/projectActionIsReferenced.memo.js +38 -0
- package/dist/rules/issues/actions/projectActionIsReferenced.memo.js.map +1 -0
- package/dist/rules/issues/attributes/noReferenceAttributeInInstanceRule.js +2 -2
- package/dist/rules/issues/attributes/noReferenceAttributeInInstanceRule.js.map +1 -1
- package/dist/rules/issues/components/componentIsReferenced.memo.d.ts +3 -0
- package/dist/rules/issues/components/componentIsReferenced.memo.js +25 -0
- package/dist/rules/issues/components/componentIsReferenced.memo.js.map +1 -0
- package/dist/rules/issues/components/noReferenceComponentRule.d.ts +1 -3
- package/dist/rules/issues/components/noReferenceComponentRule.js +8 -26
- package/dist/rules/issues/components/noReferenceComponentRule.js.map +1 -1
- package/dist/rules/issues/dom/nonEmptyVoidElementRule.js +1 -1
- package/dist/rules/issues/dom/nonEmptyVoidElementRule.js.map +1 -1
- package/dist/rules/issues/formulas/noReferenceProjectFormulaRule.js +2 -85
- package/dist/rules/issues/formulas/noReferenceProjectFormulaRule.js.map +1 -1
- package/dist/rules/issues/formulas/projectFormulaIsReferenced.memo.d.ts +3 -0
- package/dist/rules/issues/formulas/projectFormulaIsReferenced.memo.js +84 -0
- package/dist/rules/issues/formulas/projectFormulaIsReferenced.memo.js.map +1 -0
- package/dist/rules/issues/miscellaneous/createStaticSizeConstraintRule.js +1 -1
- package/dist/rules/issues/miscellaneous/createStaticSizeConstraintRule.js.map +1 -1
- package/dist/rules/issues/miscellaneous/miscRules.index.js +2 -0
- package/dist/rules/issues/miscellaneous/miscRules.index.js.map +1 -1
- package/dist/rules/issues/miscellaneous/noReferencePackageRule.d.ts +5 -0
- package/dist/rules/issues/miscellaneous/noReferencePackageRule.js +54 -0
- package/dist/rules/issues/miscellaneous/noReferencePackageRule.js.map +1 -0
- package/dist/rules/issues/style/styleRules.index.d.ts +3 -1
- package/dist/rules/issues/style/unknownCSSVariable.d.ts +3 -2
- package/dist/rules/issues/style/unknownCSSVariable.js +79 -36
- package/dist/rules/issues/style/unknownCSSVariable.js.map +1 -1
- package/dist/searchProject.js +23 -0
- package/dist/searchProject.js.map +1 -1
- package/dist/types.d.ts +11 -4
- package/package.json +3 -3
- package/src/rules/issues/actions/noReferenceProjectActionRule.ts +2 -32
- package/src/rules/issues/actions/projectActionIsReferenced.memo.ts +52 -0
- package/src/rules/issues/attributes/noReferenceAttributeInInstanceRule.ts +2 -2
- package/src/rules/issues/components/componentIsReferenced.memo.ts +39 -0
- package/src/rules/issues/components/noReferenceComponentRule.ts +10 -39
- package/src/rules/issues/dom/nonEmptyVoidElementRule.ts +1 -1
- package/src/rules/issues/formulas/noReferenceProjectFormulaRule.test.ts +33 -0
- package/src/rules/issues/formulas/noReferenceProjectFormulaRule.ts +2 -102
- package/src/rules/issues/formulas/projectFormulaIsReferenced.memo.ts +112 -0
- package/src/rules/issues/miscellaneous/createStaticSizeConstraintRule.ts +1 -1
- package/src/rules/issues/miscellaneous/miscRules.index.ts +2 -0
- package/src/rules/issues/miscellaneous/noReferencePackageRule.test.ts +289 -0
- package/src/rules/issues/miscellaneous/noReferencePackageRule.ts +72 -0
- package/src/rules/issues/style/unknownCSSVariable.test.ts +107 -44
- package/src/rules/issues/style/unknownCSSVariable.ts +101 -46
- package/src/searchProject.ts +24 -0
- 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
|
-
|
|
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'
|