@nordcraft/search 1.0.46 → 1.0.48
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 +2 -0
- package/dist/problems.worker.js.map +1 -1
- package/dist/rules/attributes/unknownComponentAttributeRule.js +29 -0
- package/dist/rules/attributes/unknownComponentAttributeRule.js.map +1 -0
- package/dist/rules/attributes/unknownComponentAttributeRule.test.js +148 -0
- package/dist/rules/attributes/unknownComponentAttributeRule.test.js.map +1 -0
- package/dist/rules/logic/noStaticNodeCondition.test.js +20 -2
- package/dist/rules/logic/noStaticNodeCondition.test.js.map +1 -1
- package/dist/rules/noReferenceNodeRule.js +2 -2
- package/dist/rules/noReferenceNodeRule.js.map +1 -1
- package/dist/rules/noReferenceNodeRule.test.js +87 -0
- package/dist/rules/noReferenceNodeRule.test.js.map +1 -1
- package/dist/rules/workflows/noPostNavigateAction.js +13 -2
- package/dist/rules/workflows/noPostNavigateAction.js.map +1 -1
- package/dist/rules/workflows/noPostNavigateAction.test.js +102 -1
- package/dist/rules/workflows/noPostNavigateAction.test.js.map +1 -1
- package/dist/searchProject.js +22 -0
- package/dist/searchProject.js.map +1 -1
- package/dist/util/helpers.test.js +1 -1
- package/dist/util/helpers.test.js.map +1 -1
- package/dist/util/removeUnused.fix.js +22 -7
- package/dist/util/removeUnused.fix.js.map +1 -1
- package/package.json +2 -2
- package/src/problems.worker.ts +2 -0
- package/src/rules/attributes/unknownComponentAttributeRule.test.ts +159 -0
- package/src/rules/attributes/unknownComponentAttributeRule.ts +37 -0
- package/src/rules/logic/noStaticNodeCondition.test.ts +20 -2
- package/src/rules/noReferenceNodeRule.test.ts +87 -0
- package/src/rules/noReferenceNodeRule.ts +2 -2
- package/src/rules/workflows/noPostNavigateAction.test.ts +111 -1
- package/src/rules/workflows/noPostNavigateAction.ts +20 -3
- package/src/searchProject.ts +24 -0
- package/src/types.d.ts +19 -2
- package/src/util/helpers.test.ts +1 -1
- package/src/util/removeUnused.fix.ts +27 -14
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import type { ElementNodeModel } from '@nordcraft/core/dist/component/component.types'
|
|
2
|
+
import type { ProjectFiles } from '@nordcraft/ssr/dist/ssr.types'
|
|
1
3
|
import { describe, expect, test } from 'bun:test'
|
|
4
|
+
import { fixProject } from '../../fixProject'
|
|
2
5
|
import { searchProject } from '../../searchProject'
|
|
3
6
|
import { noPostNavigateAction } from './noPostNavigateAction'
|
|
4
7
|
|
|
5
|
-
describe('noPostNavigateAction', () => {
|
|
8
|
+
describe('find noPostNavigateAction', () => {
|
|
6
9
|
test('should detect actions after a url navigate action', () => {
|
|
7
10
|
const problems = Array.from(
|
|
8
11
|
searchProject({
|
|
@@ -457,3 +460,110 @@ describe('noPostNavigateAction', () => {
|
|
|
457
460
|
expect(problems).toHaveLength(0)
|
|
458
461
|
})
|
|
459
462
|
})
|
|
463
|
+
|
|
464
|
+
describe('fix noPostNavigateAction', () => {
|
|
465
|
+
test('should remove actions after a url navigate action', () => {
|
|
466
|
+
const files: ProjectFiles = {
|
|
467
|
+
formulas: {},
|
|
468
|
+
components: {
|
|
469
|
+
nav: {
|
|
470
|
+
attributes: {},
|
|
471
|
+
events: [],
|
|
472
|
+
formulas: {},
|
|
473
|
+
nodes: {
|
|
474
|
+
root: {
|
|
475
|
+
type: 'element',
|
|
476
|
+
tag: 'div',
|
|
477
|
+
attrs: {},
|
|
478
|
+
classes: {},
|
|
479
|
+
events: {},
|
|
480
|
+
children: ['wkPXF99C3qdRgZmUcnHO_'],
|
|
481
|
+
style: {},
|
|
482
|
+
},
|
|
483
|
+
wkPXF99C3qdRgZmUcnHO_: {
|
|
484
|
+
tag: 'button',
|
|
485
|
+
type: 'element',
|
|
486
|
+
attrs: {},
|
|
487
|
+
style: {
|
|
488
|
+
color: 'var(--grey-200, #E5E5E5)',
|
|
489
|
+
'border-radius': '6px',
|
|
490
|
+
'background-color': 'var(--blue-600, #2563EB)',
|
|
491
|
+
'padding-left': '8px',
|
|
492
|
+
'padding-right': '8px',
|
|
493
|
+
'padding-top': '8px',
|
|
494
|
+
'padding-bottom': '8px',
|
|
495
|
+
width: 'fit-content',
|
|
496
|
+
cursor: 'pointer',
|
|
497
|
+
},
|
|
498
|
+
events: {
|
|
499
|
+
click: {
|
|
500
|
+
trigger: 'click',
|
|
501
|
+
actions: [
|
|
502
|
+
{
|
|
503
|
+
name: '@toddle/gotToURL',
|
|
504
|
+
arguments: [
|
|
505
|
+
{
|
|
506
|
+
name: 'URL',
|
|
507
|
+
formula: {
|
|
508
|
+
type: 'value',
|
|
509
|
+
value: 'https://example.com',
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
],
|
|
513
|
+
label: 'Go to URL',
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
name: '@toddle/logToConsole',
|
|
517
|
+
arguments: [
|
|
518
|
+
{
|
|
519
|
+
name: 'Label',
|
|
520
|
+
formula: {
|
|
521
|
+
type: 'value',
|
|
522
|
+
value: '',
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
name: 'Data',
|
|
527
|
+
formula: {
|
|
528
|
+
type: 'value',
|
|
529
|
+
value: '<Data>',
|
|
530
|
+
},
|
|
531
|
+
},
|
|
532
|
+
],
|
|
533
|
+
label: 'Log to console',
|
|
534
|
+
},
|
|
535
|
+
],
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
classes: {},
|
|
539
|
+
children: ['zFhmZR6YnLy089HmlK-Yn'],
|
|
540
|
+
variants: [],
|
|
541
|
+
},
|
|
542
|
+
'zFhmZR6YnLy089HmlK-Yn': {
|
|
543
|
+
type: 'text',
|
|
544
|
+
value: {
|
|
545
|
+
type: 'value',
|
|
546
|
+
value: 'Button',
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
},
|
|
550
|
+
variables: {},
|
|
551
|
+
apis: {},
|
|
552
|
+
name: 'nav',
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
}
|
|
556
|
+
const fixedProject = fixProject({
|
|
557
|
+
files,
|
|
558
|
+
rule: noPostNavigateAction,
|
|
559
|
+
fixType: 'delete-following-actions',
|
|
560
|
+
})
|
|
561
|
+
expect(
|
|
562
|
+
(
|
|
563
|
+
fixedProject.components.nav?.nodes[
|
|
564
|
+
'wkPXF99C3qdRgZmUcnHO_'
|
|
565
|
+
] as ElementNodeModel
|
|
566
|
+
)?.events?.click?.actions,
|
|
567
|
+
).toHaveLength(1)
|
|
568
|
+
})
|
|
569
|
+
})
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { get } from '@nordcraft/core/dist/utils/collections'
|
|
2
|
-
import type { Rule } from '../../types'
|
|
1
|
+
import { get, set } from '@nordcraft/core/dist/utils/collections'
|
|
2
|
+
import type { ActionModelNode, Rule } from '../../types'
|
|
3
3
|
|
|
4
4
|
export const noPostNavigateAction: Rule<{ parameter: string }> = {
|
|
5
5
|
code: 'no post navigate action',
|
|
@@ -25,7 +25,24 @@ export const noPostNavigateAction: Rule<{ parameter: string }> = {
|
|
|
25
25
|
const actionIndex = Number(_actionIndex)
|
|
26
26
|
if (actionIndex < actions.length - 1) {
|
|
27
27
|
// If the action is not the last one in the array, report it
|
|
28
|
-
report(path)
|
|
28
|
+
report(path, undefined, ['delete-following-actions'])
|
|
29
29
|
}
|
|
30
30
|
},
|
|
31
|
+
fixes: {
|
|
32
|
+
'delete-following-actions': ({ path, files }: ActionModelNode) => {
|
|
33
|
+
const actionsArrayPath = path.slice(0, -1).map((p) => String(p))
|
|
34
|
+
const actions = get(files, actionsArrayPath)
|
|
35
|
+
const actionIndex = path.at(-1)
|
|
36
|
+
if (actionIndex === undefined || !Array.isArray(actions)) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
return set(
|
|
40
|
+
files,
|
|
41
|
+
actionsArrayPath,
|
|
42
|
+
actions.slice(0, Number(actionIndex) + 1),
|
|
43
|
+
)
|
|
44
|
+
},
|
|
45
|
+
},
|
|
31
46
|
}
|
|
47
|
+
|
|
48
|
+
export type NoPostNavigateActionRuleFix = 'delete-following-actions'
|
package/src/searchProject.ts
CHANGED
|
@@ -480,6 +480,30 @@ function* visitNode({
|
|
|
480
480
|
state,
|
|
481
481
|
fixOptions: fixOptions as any,
|
|
482
482
|
})
|
|
483
|
+
if (
|
|
484
|
+
value.nodes[key].type === 'component' ||
|
|
485
|
+
value.nodes[key].type === 'element'
|
|
486
|
+
) {
|
|
487
|
+
// Visit attributes on component and element nodes
|
|
488
|
+
for (const attrKey in value.nodes[key].attrs) {
|
|
489
|
+
const attr = value.nodes[key].attrs[attrKey]
|
|
490
|
+
yield* visitNode({
|
|
491
|
+
args: {
|
|
492
|
+
nodeType: 'component-node-attribute',
|
|
493
|
+
value: { key: attrKey, value: attr },
|
|
494
|
+
path: [...path, 'nodes', key, 'attrs', attrKey],
|
|
495
|
+
rules,
|
|
496
|
+
files,
|
|
497
|
+
pathsToVisit,
|
|
498
|
+
useExactPaths,
|
|
499
|
+
memo,
|
|
500
|
+
node: value.nodes[key],
|
|
501
|
+
},
|
|
502
|
+
state,
|
|
503
|
+
fixOptions: fixOptions as any,
|
|
504
|
+
})
|
|
505
|
+
}
|
|
506
|
+
}
|
|
483
507
|
}
|
|
484
508
|
|
|
485
509
|
for (const {
|
package/src/types.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
ComponentEvent as _ComponentEvent,
|
|
7
7
|
ActionModel,
|
|
8
8
|
Component,
|
|
9
|
+
ComponentNodeModel,
|
|
9
10
|
ElementNodeModel,
|
|
10
11
|
NodeModel,
|
|
11
12
|
StyleVariant,
|
|
@@ -25,14 +26,16 @@ import type { LegacyActionRuleFix } from './rules/actions/legacyActionRule'
|
|
|
25
26
|
import type { NoReferenceProjectActionRuleFix } from './rules/actions/noReferenceProjectActionRule'
|
|
26
27
|
import type { NoReferenceApiRuleFix } from './rules/apis/noReferenceApiRule'
|
|
27
28
|
import type { NoReferenceAttributeRuleFix } from './rules/attributes/noReferenceAttributeRule'
|
|
29
|
+
import type { UnknownComponentAttributeRuleFix } from './rules/attributes/unknownComponentAttributeRule'
|
|
28
30
|
import type { NoReferenceComponentRuleFix } from './rules/components/noReferenceComponentRule'
|
|
29
31
|
import type { NoReferenceEventRuleFix } from './rules/events/noReferenceEventRule'
|
|
30
32
|
import type { LegacyFormulaRuleFix } from './rules/formulas/legacyFormulaRule'
|
|
31
33
|
import type { NoReferenceComponentFormulaRuleFix } from './rules/formulas/noReferenceComponentFormulaRule'
|
|
32
34
|
import type { NoReferenceProjectFormulaRuleFix } from './rules/formulas/noReferenceProjectFormulaRule'
|
|
33
|
-
import type { NoStaticNodeConditionRuleFix } from './rules/logic/
|
|
35
|
+
import type { NoStaticNodeConditionRuleFix } from './rules/logic/noStaticNodeCondition'
|
|
34
36
|
import type { NoReferenceNodeRuleFix } from './rules/noReferenceNodeRule'
|
|
35
37
|
import type { InvalidStyleSyntaxRuleFix } from './rules/style/invalidStyleSyntaxRule'
|
|
38
|
+
import type { NoPostNavigateActionRuleFix } from './rules/workflows/noPostNavigateAction'
|
|
36
39
|
|
|
37
40
|
type Code =
|
|
38
41
|
| 'duplicate event trigger'
|
|
@@ -75,6 +78,7 @@ type Code =
|
|
|
75
78
|
| 'unknown api'
|
|
76
79
|
| 'unknown attribute'
|
|
77
80
|
| 'unknown classname'
|
|
81
|
+
| 'unknown component attribute'
|
|
78
82
|
| 'unknown component formula input'
|
|
79
83
|
| 'unknown component slot'
|
|
80
84
|
| 'unknown context formula'
|
|
@@ -219,6 +223,10 @@ type ComponentWorkflowNode = {
|
|
|
219
223
|
testValue: any
|
|
220
224
|
}[]
|
|
221
225
|
| null
|
|
226
|
+
callbacks?: {
|
|
227
|
+
name: string
|
|
228
|
+
testValue: any
|
|
229
|
+
}[]
|
|
222
230
|
actions: ActionModel[]
|
|
223
231
|
exposeInContext?: boolean | undefined
|
|
224
232
|
}
|
|
@@ -248,6 +256,12 @@ type ComponentVariableNode = {
|
|
|
248
256
|
component: ToddleComponent<Function>
|
|
249
257
|
} & Base
|
|
250
258
|
|
|
259
|
+
type ComponentNodeAttributeNode = {
|
|
260
|
+
nodeType: 'component-node-attribute'
|
|
261
|
+
value: { key: string; value?: Formula }
|
|
262
|
+
node: ComponentNodeModel | ElementNodeModel
|
|
263
|
+
} & Base
|
|
264
|
+
|
|
251
265
|
type ComponentAttributeNode = {
|
|
252
266
|
nodeType: 'component-attribute'
|
|
253
267
|
value: Component['attributes'][0]
|
|
@@ -320,6 +334,7 @@ export type NodeType =
|
|
|
320
334
|
| ComponentEvent
|
|
321
335
|
| ComponentFormulaNode
|
|
322
336
|
| ComponentNode
|
|
337
|
+
| ComponentNodeAttributeNode
|
|
323
338
|
| ComponentNodeNode
|
|
324
339
|
| ComponentVariableNode
|
|
325
340
|
| ComponentWorkflowNode
|
|
@@ -328,8 +343,8 @@ export type NodeType =
|
|
|
328
343
|
| ProjectApiService
|
|
329
344
|
| ProjectConfigNode
|
|
330
345
|
| ProjectFormulaNode
|
|
331
|
-
| ProjectThemeNode
|
|
332
346
|
| ProjectRoute
|
|
347
|
+
| ProjectThemeNode
|
|
333
348
|
| StyleNode
|
|
334
349
|
| StyleVariantNode
|
|
335
350
|
|
|
@@ -346,6 +361,8 @@ type FixType =
|
|
|
346
361
|
| NoReferenceNodeRuleFix
|
|
347
362
|
| InvalidStyleSyntaxRuleFix
|
|
348
363
|
| NoStaticNodeConditionRuleFix
|
|
364
|
+
| NoPostNavigateActionRuleFix
|
|
365
|
+
| UnknownComponentAttributeRuleFix
|
|
349
366
|
|
|
350
367
|
export interface Rule<T = unknown, V = NodeType> {
|
|
351
368
|
category: Category
|
package/src/util/helpers.test.ts
CHANGED
|
@@ -53,7 +53,7 @@ describe('shouldSearchPath', () => {
|
|
|
53
53
|
})
|
|
54
54
|
|
|
55
55
|
describe('shouldSearchExactPath', () => {
|
|
56
|
-
test
|
|
56
|
+
test('should only return true for exact matches', () => {
|
|
57
57
|
const pathsToVisit = [
|
|
58
58
|
['components', 'Button'],
|
|
59
59
|
['components', 'Input'],
|
|
@@ -6,28 +6,41 @@ export const removeFromPathFix: FixFunction<NodeType> = ({ path, files }) =>
|
|
|
6
6
|
omit(files, path)
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Same as removeFromPathFix, but also
|
|
9
|
+
* Same as removeFromPathFix, but also cleans up any references to the removed node.
|
|
10
10
|
*/
|
|
11
11
|
export const removeNodeFromPathFix: FixFunction<NodeType> = (data) => {
|
|
12
12
|
if (data.nodeType !== 'component-node') {
|
|
13
13
|
throw new Error('removeNodeFromPathFix can only be used on component nodes')
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const componentNodesPath = data.path.
|
|
17
|
-
const
|
|
18
|
-
const nodes = get(
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
>
|
|
16
|
+
const componentNodesPath = data.path.map(String)
|
|
17
|
+
const nodeId = componentNodesPath.pop()!
|
|
18
|
+
const nodes = get(data.files, componentNodesPath) as Record<string, NodeModel>
|
|
19
|
+
|
|
20
|
+
// Clean parent reference
|
|
22
21
|
for (const key in nodes) {
|
|
23
|
-
if (
|
|
24
|
-
nodes[key].children
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
if (nodes[key].children?.includes(nodeId)) {
|
|
23
|
+
nodes[key].children = nodes[key].children.filter((p) => p !== nodeId)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Clean children recursively
|
|
28
|
+
const removeNodeAndChildren = (nodeId: string) => {
|
|
29
|
+
const node = nodes[nodeId] as NodeModel | undefined
|
|
30
|
+
if (!node) {
|
|
31
|
+
return
|
|
29
32
|
}
|
|
33
|
+
|
|
34
|
+
if (node.children) {
|
|
35
|
+
for (const childId of node.children) {
|
|
36
|
+
removeNodeAndChildren(childId)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
delete nodes[nodeId]
|
|
30
41
|
}
|
|
31
42
|
|
|
32
|
-
|
|
43
|
+
removeNodeAndChildren(nodeId)
|
|
44
|
+
|
|
45
|
+
return set(data.files, componentNodesPath, nodes)
|
|
33
46
|
}
|