@nordcraft/search 1.0.44 → 1.0.46
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/problems.worker.js +13 -5
- package/dist/problems.worker.js.map +1 -1
- package/dist/rules/actions/legacyActionRule.test.js +3 -3
- package/dist/rules/actions/legacyActionRule.test.js.map +1 -1
- package/dist/rules/events/duplicateEventTriggerRule.js +2 -1
- package/dist/rules/events/duplicateEventTriggerRule.js.map +1 -1
- package/dist/rules/logic/noStaticNodeCondition.js +29 -0
- package/dist/rules/logic/noStaticNodeCondition.js.map +1 -0
- package/dist/rules/logic/noStaticNodeCondition.test.js +274 -0
- package/dist/rules/logic/noStaticNodeCondition.test.js.map +1 -0
- package/dist/rules/logic/noUnnecessaryConditionFalsy.js +5 -1
- package/dist/rules/logic/noUnnecessaryConditionFalsy.js.map +1 -1
- package/dist/rules/logic/noUnnecessaryConditionTruthy.js +8 -4
- package/dist/rules/logic/noUnnecessaryConditionTruthy.js.map +1 -1
- package/dist/rules/logic/noUnnecessaryConditionTruthy.test.js +33 -4
- package/dist/rules/logic/noUnnecessaryConditionTruthy.test.js.map +1 -1
- package/dist/rules/noReferenceNodeRule.js +24 -0
- package/dist/rules/noReferenceNodeRule.js.map +1 -0
- package/dist/rules/noReferenceNodeRule.test.js +131 -0
- package/dist/rules/noReferenceNodeRule.test.js.map +1 -0
- package/dist/rules/style/invalidStyleSyntaxRule.js +28 -0
- package/dist/rules/style/invalidStyleSyntaxRule.js.map +1 -0
- package/dist/rules/style/invalidStyleSyntaxRule.test.js +100 -0
- package/dist/rules/style/invalidStyleSyntaxRule.test.js.map +1 -0
- package/dist/searchProject.js +22 -1
- package/dist/searchProject.js.map +1 -1
- package/dist/util/contextlessEvaluateFormula.js +77 -0
- package/dist/util/contextlessEvaluateFormula.js.map +1 -0
- package/dist/util/contextlessEvaluateFormula.test.js +152 -0
- package/dist/util/contextlessEvaluateFormula.test.js.map +1 -0
- package/dist/util/removeUnused.fix.js +18 -1
- package/dist/util/removeUnused.fix.js.map +1 -1
- package/package.json +4 -3
- package/src/problems.worker.ts +20 -7
- package/src/rules/actions/legacyActionRule.test.ts +3 -3
- package/src/rules/events/duplicateEventTriggerRule.ts +2 -1
- package/src/rules/logic/noStaticNodeCondition.test.ts +290 -0
- package/src/rules/logic/noStaticNodeCondition.ts +43 -0
- package/src/rules/logic/noUnnecessaryConditionFalsy.ts +5 -4
- package/src/rules/logic/noUnnecessaryConditionTruthy.test.ts +37 -4
- package/src/rules/logic/noUnnecessaryConditionTruthy.ts +10 -8
- package/src/rules/noReferenceNodeRule.test.ts +140 -0
- package/src/rules/noReferenceNodeRule.ts +34 -0
- package/src/rules/style/invalidStyleSyntaxRule.test.ts +106 -0
- package/src/rules/style/invalidStyleSyntaxRule.ts +35 -0
- package/src/searchProject.ts +25 -1
- package/src/types.d.ts +20 -2
- package/src/util/contextlessEvaluateFormula.test.ts +190 -0
- package/src/util/contextlessEvaluateFormula.ts +110 -0
- package/src/util/removeUnused.fix.ts +29 -1
- package/dist/memos/getAllCustomPropertiesBySyntax.js +0 -43
- package/dist/memos/getAllCustomPropertiesBySyntax.js.map +0 -1
- package/dist/rules/style-variables/ambiguousStyleVariableSyntaxRule.js +0 -50
- package/dist/rules/style-variables/ambiguousStyleVariableSyntaxRule.js.map +0 -1
- package/dist/rules/style-variables/ambiguousStyleVariableSyntaxRule.test.js +0 -265
- package/dist/rules/style-variables/ambiguousStyleVariableSyntaxRule.test.js.map +0 -1
- package/src/memos/getAllCustomPropertiesBySyntax.ts +0 -68
- package/src/rules/style-variables/ambiguousStyleVariableSyntaxRule.test.ts +0 -278
- package/src/rules/style-variables/ambiguousStyleVariableSyntaxRule.ts +0 -65
|
@@ -1,3 +1,20 @@
|
|
|
1
|
-
import { omit } from '@nordcraft/core/dist/utils/collections';
|
|
1
|
+
import { get, omit, set } from '@nordcraft/core/dist/utils/collections';
|
|
2
2
|
export const removeFromPathFix = ({ path, files }) => omit(files, path);
|
|
3
|
+
/**
|
|
4
|
+
* Same as removeFromPathFix, but also removes the node from its parent's children.
|
|
5
|
+
*/
|
|
6
|
+
export const removeNodeFromPathFix = (data) => {
|
|
7
|
+
if (data.nodeType !== 'component-node') {
|
|
8
|
+
throw new Error('removeNodeFromPathFix can only be used on component nodes');
|
|
9
|
+
}
|
|
10
|
+
const componentNodesPath = data.path.slice(0, -1).map(String);
|
|
11
|
+
const filesWithoutNode = removeFromPathFix(data);
|
|
12
|
+
const nodes = get(filesWithoutNode, componentNodesPath);
|
|
13
|
+
for (const key in nodes) {
|
|
14
|
+
if (nodes[key].children?.includes(data.path[data.path.length - 1])) {
|
|
15
|
+
nodes[key].children = nodes[key].children.filter((p) => p !== data.path[data.path.length - 1]);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return set(filesWithoutNode, componentNodesPath, nodes);
|
|
19
|
+
};
|
|
3
20
|
//# sourceMappingURL=removeUnused.fix.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"removeUnused.fix.js","sourceRoot":"","sources":["../../src/util/removeUnused.fix.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"removeUnused.fix.js","sourceRoot":"","sources":["../../src/util/removeUnused.fix.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,wCAAwC,CAAA;AAGvE,MAAM,CAAC,MAAM,iBAAiB,GAA0B,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAC1E,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;AAEnB;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAA0B,CAAC,IAAI,EAAE,EAAE;IACnE,IAAI,IAAI,CAAC,QAAQ,KAAK,gBAAgB,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAA;IAC9E,CAAC;IAED,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAC7D,MAAM,gBAAgB,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAA;IAChD,MAAM,KAAK,GAAG,GAAG,CAAC,gBAAgB,EAAE,kBAAkB,CAGrD,CAAA;IACD,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE,CAAC;QACxB,IACE,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAW,CAAC,EACxE,CAAC;YACD,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,CAC9C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAC7C,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO,GAAG,CAAC,gBAAgB,EAAE,kBAAkB,EAAE,KAAK,CAAC,CAAA;AACzD,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/nordcraftengine/nordcraft",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@nordcraft/ssr": "1.0.
|
|
9
|
-
"jsondiffpatch": "0.7.3"
|
|
8
|
+
"@nordcraft/ssr": "1.0.46",
|
|
9
|
+
"jsondiffpatch": "0.7.3",
|
|
10
|
+
"postcss": "8.5.6"
|
|
10
11
|
},
|
|
11
12
|
"scripts": {
|
|
12
13
|
"build": "tsc",
|
|
@@ -18,5 +19,5 @@
|
|
|
18
19
|
"test:watch:only": "bun test --watch --only"
|
|
19
20
|
},
|
|
20
21
|
"files": ["dist", "src"],
|
|
21
|
-
"version": "1.0.
|
|
22
|
+
"version": "1.0.46"
|
|
22
23
|
}
|
package/src/problems.worker.ts
CHANGED
|
@@ -37,12 +37,14 @@ import { unknownFormulaRule } from './rules/formulas/unknownFormulaRule'
|
|
|
37
37
|
import { unknownProjectFormulaRule } from './rules/formulas/unknownProjectFormulaRule'
|
|
38
38
|
import { unknownRepeatIndexFormulaRule } from './rules/formulas/unknownRepeatIndexFormulaRule'
|
|
39
39
|
import { unknownRepeatItemFormulaRule } from './rules/formulas/unknownRepeatItemFormulaRule'
|
|
40
|
+
import { noStaticNodeCondition } from './rules/logic/noStaticNodeCondition'
|
|
40
41
|
import { noUnnecessaryConditionFalsy } from './rules/logic/noUnnecessaryConditionFalsy'
|
|
41
42
|
import { noUnnecessaryConditionTruthy } from './rules/logic/noUnnecessaryConditionTruthy'
|
|
43
|
+
import { noReferenceNodeRule } from './rules/noReferenceNodeRule'
|
|
42
44
|
import { requireExtensionRule } from './rules/requireExtensionRule'
|
|
43
45
|
import { unknownClassnameRule } from './rules/slots/unknownClassnameRule'
|
|
44
46
|
import { unknownComponentSlotRule } from './rules/slots/unknownComponentSlotRule'
|
|
45
|
-
import {
|
|
47
|
+
import { invalidStyleSyntaxRule } from './rules/style/invalidStyleSyntaxRule'
|
|
46
48
|
import { unknownCookieRule } from './rules/unknownCookieRule'
|
|
47
49
|
import { duplicateUrlParameterRule } from './rules/urlParameters/duplicateUrlParameterRule'
|
|
48
50
|
import { unknownSetUrlParameterRule } from './rules/urlParameters/unknownSetUrlParameterRule'
|
|
@@ -144,6 +146,7 @@ const RULES = [
|
|
|
144
146
|
duplicateWorkflowParameterRule,
|
|
145
147
|
elementWithoutInteractiveContentRule,
|
|
146
148
|
imageWithoutDimensionRule,
|
|
149
|
+
invalidStyleSyntaxRule,
|
|
147
150
|
legacyActionRule,
|
|
148
151
|
legacyApiRule,
|
|
149
152
|
legacyFormulaRule,
|
|
@@ -156,9 +159,11 @@ const RULES = [
|
|
|
156
159
|
noReferenceComponentRule,
|
|
157
160
|
noReferenceComponentWorkflowRule,
|
|
158
161
|
noReferenceEventRule,
|
|
162
|
+
noReferenceNodeRule,
|
|
159
163
|
noReferenceProjectActionRule,
|
|
160
164
|
noReferenceProjectFormulaRule,
|
|
161
165
|
noReferenceVariableRule,
|
|
166
|
+
noStaticNodeCondition,
|
|
162
167
|
noUnnecessaryConditionFalsy,
|
|
163
168
|
noUnnecessaryConditionTruthy,
|
|
164
169
|
requireExtensionRule,
|
|
@@ -190,25 +195,28 @@ const RULES = [
|
|
|
190
195
|
unknownVariableRule,
|
|
191
196
|
unknownVariableSetterRule,
|
|
192
197
|
unknownWorkflowParameterRule,
|
|
193
|
-
ambiguousStyleVariableSyntaxRule,
|
|
194
198
|
]
|
|
195
199
|
|
|
196
200
|
interface FindProblemsArgs {
|
|
201
|
+
id: string
|
|
197
202
|
files: ProjectFiles
|
|
198
203
|
options?: Options
|
|
199
204
|
}
|
|
200
205
|
|
|
201
206
|
interface FixProblemsArgs {
|
|
207
|
+
id: string
|
|
202
208
|
files: ProjectFiles
|
|
203
209
|
options?: Options
|
|
204
210
|
fixRule: Code
|
|
205
211
|
fixType: FixType
|
|
206
|
-
id: string
|
|
207
212
|
}
|
|
208
213
|
|
|
209
214
|
type Message = FindProblemsArgs | FixProblemsArgs
|
|
210
215
|
|
|
211
|
-
|
|
216
|
+
interface FindProblemsResponse {
|
|
217
|
+
id: string
|
|
218
|
+
results: Result[]
|
|
219
|
+
}
|
|
212
220
|
|
|
213
221
|
interface FixProblemsResponse {
|
|
214
222
|
id: string
|
|
@@ -223,6 +231,11 @@ const respond = (data: Response) => postMessage(data)
|
|
|
223
231
|
|
|
224
232
|
const findProblems = (data: FindProblemsArgs) => {
|
|
225
233
|
const { files, options = {} } = data
|
|
234
|
+
const idRespond = (results: Result[]) =>
|
|
235
|
+
respond({
|
|
236
|
+
id: data.id,
|
|
237
|
+
results,
|
|
238
|
+
})
|
|
226
239
|
const rules = RULES.filter(
|
|
227
240
|
(rule) =>
|
|
228
241
|
(!options.categories || options.categories.includes(rule.category)) &&
|
|
@@ -248,7 +261,7 @@ const findProblems = (data: FindProblemsArgs) => {
|
|
|
248
261
|
case 'per-file': {
|
|
249
262
|
if (fileType !== problem.path[0] || fileName !== problem.path[1]) {
|
|
250
263
|
if (batch.length > 0) {
|
|
251
|
-
|
|
264
|
+
idRespond(batch)
|
|
252
265
|
}
|
|
253
266
|
batch = []
|
|
254
267
|
fileType = problem.path[0]
|
|
@@ -262,7 +275,7 @@ const findProblems = (data: FindProblemsArgs) => {
|
|
|
262
275
|
default: {
|
|
263
276
|
batch.push(problem)
|
|
264
277
|
if (batch.length >= (options.batchSize ?? 1)) {
|
|
265
|
-
|
|
278
|
+
idRespond(batch)
|
|
266
279
|
batch = []
|
|
267
280
|
}
|
|
268
281
|
break
|
|
@@ -271,7 +284,7 @@ const findProblems = (data: FindProblemsArgs) => {
|
|
|
271
284
|
}
|
|
272
285
|
|
|
273
286
|
// Send the remaining results
|
|
274
|
-
|
|
287
|
+
idRespond(batch)
|
|
275
288
|
}
|
|
276
289
|
|
|
277
290
|
const fixProblems = (data: FixProblemsArgs) => {
|
|
@@ -197,7 +197,7 @@ describe('fix legacyActions', () => {
|
|
|
197
197
|
})
|
|
198
198
|
const fixedAction = (
|
|
199
199
|
fixedProject.components['apiComponent']?.nodes['root'] as ElementNodeModel
|
|
200
|
-
).events['click']
|
|
200
|
+
).events['click']?.actions[0]
|
|
201
201
|
expect(fixedAction).toMatchInlineSnapshot(`
|
|
202
202
|
{
|
|
203
203
|
"cases": [
|
|
@@ -321,7 +321,7 @@ describe('fix legacyActions', () => {
|
|
|
321
321
|
})
|
|
322
322
|
const fixedAction = (
|
|
323
323
|
fixedProject.components['apiComponent']?.nodes['root'] as ElementNodeModel
|
|
324
|
-
).events['click']
|
|
324
|
+
).events['click']?.actions[0]
|
|
325
325
|
expect(fixedAction).toMatchInlineSnapshot(`
|
|
326
326
|
{
|
|
327
327
|
"data": {
|
|
@@ -378,7 +378,7 @@ describe('fix legacyActions', () => {
|
|
|
378
378
|
})
|
|
379
379
|
const fixedAction = (
|
|
380
380
|
fixedProject.components['apiComponent']?.nodes['root'] as ElementNodeModel
|
|
381
|
-
).events['click']
|
|
381
|
+
).events['click']?.actions[0]
|
|
382
382
|
expect(fixedAction).toMatchInlineSnapshot(`
|
|
383
383
|
{
|
|
384
384
|
"arguments": [
|
|
@@ -10,8 +10,9 @@ export const duplicateEventTriggerRule: Rule<{ trigger: string }> = {
|
|
|
10
10
|
}
|
|
11
11
|
const eventTriggers = new Set<string>()
|
|
12
12
|
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
13
14
|
Object.entries(value.events ?? {}).forEach(([key, event]) => {
|
|
14
|
-
if (typeof event
|
|
15
|
+
if (typeof event?.trigger !== 'string') {
|
|
15
16
|
return
|
|
16
17
|
}
|
|
17
18
|
if (eventTriggers.has(event.trigger)) {
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import type { ProjectFiles } from '@nordcraft/ssr/dist/ssr.types'
|
|
2
|
+
import { describe, expect, test } from 'bun:test'
|
|
3
|
+
import { fixProject } from '../../fixProject'
|
|
4
|
+
import { searchProject } from '../../searchProject'
|
|
5
|
+
import { noStaticNodeCondition } from './noStaticNodeCondition'
|
|
6
|
+
|
|
7
|
+
describe('noStaticNodeCondition', () => {
|
|
8
|
+
test('should report node condition that is always truthy', () => {
|
|
9
|
+
const problems = Array.from(
|
|
10
|
+
searchProject({
|
|
11
|
+
files: {
|
|
12
|
+
components: {
|
|
13
|
+
test: {
|
|
14
|
+
name: 'test',
|
|
15
|
+
nodes: {
|
|
16
|
+
root: {
|
|
17
|
+
type: 'element',
|
|
18
|
+
attrs: {},
|
|
19
|
+
classes: {},
|
|
20
|
+
events: {},
|
|
21
|
+
tag: 'div',
|
|
22
|
+
children: [],
|
|
23
|
+
style: {},
|
|
24
|
+
condition: {
|
|
25
|
+
type: 'value',
|
|
26
|
+
value: true,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
formulas: {},
|
|
31
|
+
apis: {},
|
|
32
|
+
attributes: {},
|
|
33
|
+
variables: {},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
rules: [noStaticNodeCondition],
|
|
38
|
+
}),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
expect(problems).toHaveLength(1)
|
|
42
|
+
expect(problems[0].code).toBe('no-static-node-condition')
|
|
43
|
+
expect(problems[0].details).toEqual({
|
|
44
|
+
result: true,
|
|
45
|
+
})
|
|
46
|
+
expect(problems[0].fixes).toEqual(['remove-condition'])
|
|
47
|
+
expect(problems[0].path).toEqual([
|
|
48
|
+
'components',
|
|
49
|
+
'test',
|
|
50
|
+
'nodes',
|
|
51
|
+
'root',
|
|
52
|
+
'condition',
|
|
53
|
+
])
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('should report node condition that is always falsy', () => {
|
|
57
|
+
const problems = Array.from(
|
|
58
|
+
searchProject({
|
|
59
|
+
files: {
|
|
60
|
+
components: {
|
|
61
|
+
test: {
|
|
62
|
+
name: 'test',
|
|
63
|
+
nodes: {
|
|
64
|
+
root: {
|
|
65
|
+
type: 'element',
|
|
66
|
+
attrs: {},
|
|
67
|
+
classes: {},
|
|
68
|
+
events: {},
|
|
69
|
+
tag: 'div',
|
|
70
|
+
children: [],
|
|
71
|
+
style: {},
|
|
72
|
+
condition: {
|
|
73
|
+
type: 'value',
|
|
74
|
+
value: false,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
formulas: {},
|
|
79
|
+
apis: {},
|
|
80
|
+
attributes: {},
|
|
81
|
+
variables: {},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
rules: [noStaticNodeCondition],
|
|
86
|
+
}),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
expect(problems).toHaveLength(1)
|
|
90
|
+
expect(problems[0].code).toBe('no-static-node-condition')
|
|
91
|
+
expect(problems[0].details).toEqual({
|
|
92
|
+
result: false,
|
|
93
|
+
})
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('should not report node condition that is dynamic', () => {
|
|
97
|
+
const problems = Array.from(
|
|
98
|
+
searchProject({
|
|
99
|
+
files: {
|
|
100
|
+
components: {
|
|
101
|
+
test: {
|
|
102
|
+
name: 'test',
|
|
103
|
+
nodes: {
|
|
104
|
+
root: {
|
|
105
|
+
type: 'element',
|
|
106
|
+
attrs: {},
|
|
107
|
+
classes: {},
|
|
108
|
+
events: {},
|
|
109
|
+
tag: 'div',
|
|
110
|
+
children: [],
|
|
111
|
+
style: {},
|
|
112
|
+
condition: {
|
|
113
|
+
type: 'apply',
|
|
114
|
+
name: 'randomNumber',
|
|
115
|
+
arguments: [],
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
formulas: {},
|
|
120
|
+
apis: {},
|
|
121
|
+
attributes: {},
|
|
122
|
+
variables: {},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
rules: [noStaticNodeCondition],
|
|
127
|
+
}),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
expect(problems).toHaveLength(0)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
test('should fix static truthy condition by removing the condition', () => {
|
|
134
|
+
const files: ProjectFiles = {
|
|
135
|
+
components: {
|
|
136
|
+
test: {
|
|
137
|
+
name: 'test',
|
|
138
|
+
nodes: {
|
|
139
|
+
root: {
|
|
140
|
+
type: 'element',
|
|
141
|
+
attrs: {},
|
|
142
|
+
classes: {},
|
|
143
|
+
events: {},
|
|
144
|
+
tag: 'div',
|
|
145
|
+
children: [],
|
|
146
|
+
style: {},
|
|
147
|
+
condition: {
|
|
148
|
+
type: 'value',
|
|
149
|
+
value: true,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
formulas: {},
|
|
154
|
+
apis: {},
|
|
155
|
+
attributes: {},
|
|
156
|
+
variables: {},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
}
|
|
160
|
+
const fixedFiles = fixProject({
|
|
161
|
+
files,
|
|
162
|
+
rule: noStaticNodeCondition,
|
|
163
|
+
fixType: 'remove-condition',
|
|
164
|
+
})
|
|
165
|
+
expect(fixedFiles).toMatchInlineSnapshot(`
|
|
166
|
+
{
|
|
167
|
+
"components": {
|
|
168
|
+
"test": {
|
|
169
|
+
"apis": {},
|
|
170
|
+
"attributes": {},
|
|
171
|
+
"formulas": {},
|
|
172
|
+
"name": "test",
|
|
173
|
+
"nodes": {
|
|
174
|
+
"root": {
|
|
175
|
+
"attrs": {},
|
|
176
|
+
"children": [],
|
|
177
|
+
"classes": {},
|
|
178
|
+
"events": {},
|
|
179
|
+
"style": {},
|
|
180
|
+
"tag": "div",
|
|
181
|
+
"type": "element",
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
"variables": {},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
}
|
|
188
|
+
`)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
test("should fix static falsy condition by removing the node and any parents' references to it", () => {
|
|
192
|
+
const files: ProjectFiles = {
|
|
193
|
+
components: {
|
|
194
|
+
test: {
|
|
195
|
+
name: 'test',
|
|
196
|
+
nodes: {
|
|
197
|
+
root: {
|
|
198
|
+
type: 'element',
|
|
199
|
+
attrs: {},
|
|
200
|
+
classes: {},
|
|
201
|
+
events: {},
|
|
202
|
+
tag: 'div',
|
|
203
|
+
children: ['alwaysHiddenElement', 'sometimesVisibleElement'],
|
|
204
|
+
style: {},
|
|
205
|
+
},
|
|
206
|
+
alwaysHiddenElement: {
|
|
207
|
+
type: 'element',
|
|
208
|
+
attrs: {},
|
|
209
|
+
classes: {},
|
|
210
|
+
events: {},
|
|
211
|
+
tag: 'div',
|
|
212
|
+
children: [],
|
|
213
|
+
style: {},
|
|
214
|
+
condition: {
|
|
215
|
+
type: 'value',
|
|
216
|
+
value: false,
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
sometimesVisibleElement: {
|
|
220
|
+
type: 'element',
|
|
221
|
+
attrs: {},
|
|
222
|
+
classes: {},
|
|
223
|
+
events: {},
|
|
224
|
+
tag: 'div',
|
|
225
|
+
children: [],
|
|
226
|
+
style: {},
|
|
227
|
+
condition: {
|
|
228
|
+
type: 'path',
|
|
229
|
+
path: ['Variables', 'maybe'],
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
formulas: {},
|
|
234
|
+
apis: {},
|
|
235
|
+
attributes: {},
|
|
236
|
+
variables: {},
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const fixedFiles = fixProject({
|
|
242
|
+
files,
|
|
243
|
+
rule: noStaticNodeCondition,
|
|
244
|
+
fixType: 'remove-node',
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
expect(fixedFiles).toMatchInlineSnapshot(`
|
|
248
|
+
{
|
|
249
|
+
"components": {
|
|
250
|
+
"test": {
|
|
251
|
+
"apis": {},
|
|
252
|
+
"attributes": {},
|
|
253
|
+
"formulas": {},
|
|
254
|
+
"name": "test",
|
|
255
|
+
"nodes": {
|
|
256
|
+
"root": {
|
|
257
|
+
"attrs": {},
|
|
258
|
+
"children": [
|
|
259
|
+
"sometimesVisibleElement",
|
|
260
|
+
],
|
|
261
|
+
"classes": {},
|
|
262
|
+
"events": {},
|
|
263
|
+
"style": {},
|
|
264
|
+
"tag": "div",
|
|
265
|
+
"type": "element",
|
|
266
|
+
},
|
|
267
|
+
"sometimesVisibleElement": {
|
|
268
|
+
"attrs": {},
|
|
269
|
+
"children": [],
|
|
270
|
+
"classes": {},
|
|
271
|
+
"condition": {
|
|
272
|
+
"path": [
|
|
273
|
+
"Variables",
|
|
274
|
+
"maybe",
|
|
275
|
+
],
|
|
276
|
+
"type": "path",
|
|
277
|
+
},
|
|
278
|
+
"events": {},
|
|
279
|
+
"style": {},
|
|
280
|
+
"tag": "div",
|
|
281
|
+
"type": "element",
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
"variables": {},
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
}
|
|
288
|
+
`)
|
|
289
|
+
})
|
|
290
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { FixFunction, NodeType, Rule } from '../../types'
|
|
2
|
+
import { contextlessEvaluateFormula } from '../../util/contextlessEvaluateFormula'
|
|
3
|
+
import {
|
|
4
|
+
removeFromPathFix,
|
|
5
|
+
removeNodeFromPathFix,
|
|
6
|
+
} from '../../util/removeUnused.fix'
|
|
7
|
+
|
|
8
|
+
export const noStaticNodeCondition: Rule<{
|
|
9
|
+
result: ReturnType<typeof contextlessEvaluateFormula>['result']
|
|
10
|
+
}> = {
|
|
11
|
+
code: 'no-static-node-condition',
|
|
12
|
+
level: 'warning',
|
|
13
|
+
category: 'Quality',
|
|
14
|
+
visit: (report, { path: nodePath, value, nodeType }) => {
|
|
15
|
+
if (nodeType !== 'component-node' || !value.condition) {
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const { isStatic, result } = contextlessEvaluateFormula(value.condition)
|
|
20
|
+
if (isStatic) {
|
|
21
|
+
const conditionPath = [...nodePath, 'condition']
|
|
22
|
+
// - if truthy: "Condition is always true, you can safely remove the condition as it will always be rendered."
|
|
23
|
+
// - if falsy: "Condition is always false, you can safely remove the entire node as it will never be rendered."
|
|
24
|
+
report(
|
|
25
|
+
conditionPath,
|
|
26
|
+
{
|
|
27
|
+
result,
|
|
28
|
+
},
|
|
29
|
+
Boolean(result) === true ? ['remove-condition'] : ['remove-node'],
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
fixes: {
|
|
34
|
+
'remove-condition': removeFromPathFix,
|
|
35
|
+
'remove-node': (({ path, ...data }) =>
|
|
36
|
+
removeNodeFromPathFix({
|
|
37
|
+
path: path.slice(0, -1),
|
|
38
|
+
...data,
|
|
39
|
+
})) as FixFunction<NodeType>,
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type NoStaticNodeConditionRuleFix = 'remove-condition' | 'remove-node'
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Rule } from '../../types'
|
|
2
|
+
import { contextlessEvaluateFormula } from '../../util/contextlessEvaluateFormula'
|
|
2
3
|
|
|
3
4
|
export const noUnnecessaryConditionFalsy: Rule = {
|
|
4
5
|
code: 'no-unnecessary-condition-falsy',
|
|
@@ -10,10 +11,10 @@ export const noUnnecessaryConditionFalsy: Rule = {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if (
|
|
13
|
-
value.arguments.some(
|
|
14
|
-
(arg)
|
|
15
|
-
|
|
16
|
-
)
|
|
14
|
+
value.arguments.some((arg) => {
|
|
15
|
+
const { result, isStatic } = contextlessEvaluateFormula(arg.formula)
|
|
16
|
+
return isStatic && Boolean(result) === false
|
|
17
|
+
})
|
|
17
18
|
) {
|
|
18
19
|
report(path)
|
|
19
20
|
}
|
|
@@ -32,7 +32,41 @@ describe('noUnnecessaryConditionTruthy', () => {
|
|
|
32
32
|
},
|
|
33
33
|
],
|
|
34
34
|
},
|
|
35
|
-
|
|
35
|
+
},
|
|
36
|
+
classes: {},
|
|
37
|
+
events: {},
|
|
38
|
+
tag: 'div',
|
|
39
|
+
children: [],
|
|
40
|
+
style: {},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
formulas: {},
|
|
44
|
+
apis: {},
|
|
45
|
+
attributes: {},
|
|
46
|
+
variables: {},
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
rules: [noUnnecessaryConditionTruthy],
|
|
51
|
+
}),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
expect(problems).toHaveLength(1)
|
|
55
|
+
expect(problems[0].code).toBe('no-unnecessary-condition-truthy')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('should report unnecessary truthy conditions when a value is of type object or array as they are always consider truthy', () => {
|
|
59
|
+
const problems = Array.from(
|
|
60
|
+
searchProject({
|
|
61
|
+
files: {
|
|
62
|
+
components: {
|
|
63
|
+
test: {
|
|
64
|
+
name: 'test',
|
|
65
|
+
nodes: {
|
|
66
|
+
root: {
|
|
67
|
+
type: 'element',
|
|
68
|
+
attrs: {
|
|
69
|
+
test1: {
|
|
36
70
|
type: 'or',
|
|
37
71
|
arguments: [
|
|
38
72
|
{
|
|
@@ -43,7 +77,7 @@ describe('noUnnecessaryConditionTruthy', () => {
|
|
|
43
77
|
},
|
|
44
78
|
],
|
|
45
79
|
},
|
|
46
|
-
|
|
80
|
+
test2: {
|
|
47
81
|
type: 'or',
|
|
48
82
|
arguments: [
|
|
49
83
|
{
|
|
@@ -73,10 +107,9 @@ describe('noUnnecessaryConditionTruthy', () => {
|
|
|
73
107
|
}),
|
|
74
108
|
)
|
|
75
109
|
|
|
76
|
-
expect(problems).toHaveLength(
|
|
110
|
+
expect(problems).toHaveLength(2)
|
|
77
111
|
expect(problems[0].code).toBe('no-unnecessary-condition-truthy')
|
|
78
112
|
expect(problems[1].code).toBe('no-unnecessary-condition-truthy')
|
|
79
|
-
expect(problems[2].code).toBe('no-unnecessary-condition-truthy')
|
|
80
113
|
})
|
|
81
114
|
|
|
82
115
|
test('should not report necessary truthy conditions', () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Rule } from '../../types'
|
|
2
|
+
import { contextlessEvaluateFormula } from '../../util/contextlessEvaluateFormula'
|
|
2
3
|
|
|
3
4
|
export const noUnnecessaryConditionTruthy: Rule = {
|
|
4
5
|
code: 'no-unnecessary-condition-truthy',
|
|
@@ -10,14 +11,15 @@ export const noUnnecessaryConditionTruthy: Rule = {
|
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
if (
|
|
13
|
-
value.arguments.some(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
value.arguments.some((arg) => {
|
|
15
|
+
// Objects and arrays, even empty ones, are always truthy
|
|
16
|
+
if (arg.formula.type === 'object' || arg.formula.type === 'array') {
|
|
17
|
+
return true
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const { result, isStatic } = contextlessEvaluateFormula(arg.formula)
|
|
21
|
+
return isStatic && Boolean(result) === true
|
|
22
|
+
})
|
|
21
23
|
) {
|
|
22
24
|
report(path)
|
|
23
25
|
}
|