@nordcraft/search 1.0.85 → 1.0.87

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 (257) hide show
  1. package/dist/rules/issues/actions/actionRules.index.js +4 -0
  2. package/dist/rules/issues/actions/actionRules.index.js.map +1 -1
  3. package/dist/rules/issues/actions/createActionNameRule.d.ts +5 -1
  4. package/dist/rules/issues/actions/createActionNameRule.js +6 -2
  5. package/dist/rules/issues/actions/createActionNameRule.js.map +1 -1
  6. package/dist/rules/issues/actions/duplicateActionArgumentNameRule.js +8 -1
  7. package/dist/rules/issues/actions/duplicateActionArgumentNameRule.js.map +1 -1
  8. package/dist/rules/issues/actions/legacyActionRule.js +18 -8
  9. package/dist/rules/issues/actions/legacyActionRule.js.map +1 -1
  10. package/dist/rules/issues/actions/noReferenceProjectActionRule.js +8 -1
  11. package/dist/rules/issues/actions/noReferenceProjectActionRule.js.map +1 -1
  12. package/dist/rules/issues/actions/unknownActionArgumentRule.js +23 -6
  13. package/dist/rules/issues/actions/unknownActionArgumentRule.js.map +1 -1
  14. package/dist/rules/issues/actions/unknownActionEventRule.js +11 -3
  15. package/dist/rules/issues/actions/unknownActionEventRule.js.map +1 -1
  16. package/dist/rules/issues/actions/unknownProjectActionRule.js +8 -1
  17. package/dist/rules/issues/actions/unknownProjectActionRule.js.map +1 -1
  18. package/dist/rules/issues/apis/invalidApiParserModeRule.js +8 -1
  19. package/dist/rules/issues/apis/invalidApiParserModeRule.js.map +1 -1
  20. package/dist/rules/issues/apis/invalidApiProxyBodySettingRule.js +8 -1
  21. package/dist/rules/issues/apis/invalidApiProxyBodySettingRule.js.map +1 -1
  22. package/dist/rules/issues/apis/invalidApiProxyCookieSettingRule.js +8 -1
  23. package/dist/rules/issues/apis/invalidApiProxyCookieSettingRule.js.map +1 -1
  24. package/dist/rules/issues/apis/legacyApiRule.js +8 -1
  25. package/dist/rules/issues/apis/legacyApiRule.js.map +1 -1
  26. package/dist/rules/issues/apis/noReferenceApiInputRule.js +8 -1
  27. package/dist/rules/issues/apis/noReferenceApiInputRule.js.map +1 -1
  28. package/dist/rules/issues/apis/noReferenceApiRule.js +8 -1
  29. package/dist/rules/issues/apis/noReferenceApiRule.js.map +1 -1
  30. package/dist/rules/issues/apis/noReferenceApiServiceRule.js +9 -1
  31. package/dist/rules/issues/apis/noReferenceApiServiceRule.js.map +1 -1
  32. package/dist/rules/issues/apis/unknownApiInputRule.js +8 -1
  33. package/dist/rules/issues/apis/unknownApiInputRule.js.map +1 -1
  34. package/dist/rules/issues/apis/unknownApiRule.js +16 -2
  35. package/dist/rules/issues/apis/unknownApiRule.js.map +1 -1
  36. package/dist/rules/issues/apis/unknownApiServiceRule.js +9 -1
  37. package/dist/rules/issues/apis/unknownApiServiceRule.js.map +1 -1
  38. package/dist/rules/issues/apis/unknownFetchInputRule.js +9 -1
  39. package/dist/rules/issues/apis/unknownFetchInputRule.js.map +1 -1
  40. package/dist/rules/issues/attributes/noReferenceAttributeInInstanceRule.js +9 -2
  41. package/dist/rules/issues/attributes/noReferenceAttributeInInstanceRule.js.map +1 -1
  42. package/dist/rules/issues/attributes/noReferenceAttributeRule.js +8 -1
  43. package/dist/rules/issues/attributes/noReferenceAttributeRule.js.map +1 -1
  44. package/dist/rules/issues/attributes/unknownAttributeRule.js +8 -1
  45. package/dist/rules/issues/attributes/unknownAttributeRule.js.map +1 -1
  46. package/dist/rules/issues/attributes/unknownComponentAttributeRule.js +9 -3
  47. package/dist/rules/issues/attributes/unknownComponentAttributeRule.js.map +1 -1
  48. package/dist/rules/issues/components/invalidComponentStructureRule.js +12 -4
  49. package/dist/rules/issues/components/invalidComponentStructureRule.js.map +1 -1
  50. package/dist/rules/issues/components/noReferenceComponentRule.js +8 -1
  51. package/dist/rules/issues/components/noReferenceComponentRule.js.map +1 -1
  52. package/dist/rules/issues/components/unknownComponentRule.js +8 -1
  53. package/dist/rules/issues/components/unknownComponentRule.js.map +1 -1
  54. package/dist/rules/issues/context/noContextConsumersRule.js +7 -1
  55. package/dist/rules/issues/context/noContextConsumersRule.js.map +1 -1
  56. package/dist/rules/issues/context/unknownContextFormulaRule.js +10 -3
  57. package/dist/rules/issues/context/unknownContextFormulaRule.js.map +1 -1
  58. package/dist/rules/issues/context/unknownContextProviderFormulaRule.js +10 -3
  59. package/dist/rules/issues/context/unknownContextProviderFormulaRule.js.map +1 -1
  60. package/dist/rules/issues/context/unknownContextProviderRule.js +8 -1
  61. package/dist/rules/issues/context/unknownContextProviderRule.js.map +1 -1
  62. package/dist/rules/issues/dom/createRequiredDirectChildRule.js +11 -4
  63. package/dist/rules/issues/dom/createRequiredDirectChildRule.js.map +1 -1
  64. package/dist/rules/issues/dom/createRequiredDirectParentRule.js +11 -4
  65. package/dist/rules/issues/dom/createRequiredDirectParentRule.js.map +1 -1
  66. package/dist/rules/issues/dom/createRequiredElementAttributeRule.js +8 -1
  67. package/dist/rules/issues/dom/createRequiredElementAttributeRule.js.map +1 -1
  68. package/dist/rules/issues/dom/createRequiredMetaTagRule.js +10 -1
  69. package/dist/rules/issues/dom/createRequiredMetaTagRule.js.map +1 -1
  70. package/dist/rules/issues/dom/elementWithoutInteractiveContentRule.js +16 -3
  71. package/dist/rules/issues/dom/elementWithoutInteractiveContentRule.js.map +1 -1
  72. package/dist/rules/issues/dom/imageWithoutDimensionRule.js +7 -1
  73. package/dist/rules/issues/dom/imageWithoutDimensionRule.js.map +1 -1
  74. package/dist/rules/issues/dom/nonEmptyVoidElementRule.js +8 -1
  75. package/dist/rules/issues/dom/nonEmptyVoidElementRule.js.map +1 -1
  76. package/dist/rules/issues/events/duplicateEventTriggerRule.js +8 -1
  77. package/dist/rules/issues/events/duplicateEventTriggerRule.js.map +1 -1
  78. package/dist/rules/issues/events/noReferenceEventRule.js +9 -1
  79. package/dist/rules/issues/events/noReferenceEventRule.js.map +1 -1
  80. package/dist/rules/issues/events/unknownEventRule.js +8 -1
  81. package/dist/rules/issues/events/unknownEventRule.js.map +1 -1
  82. package/dist/rules/issues/events/unknownTriggerEventRule.js +8 -1
  83. package/dist/rules/issues/events/unknownTriggerEventRule.js.map +1 -1
  84. package/dist/rules/issues/formulas/duplicateFormulaArgumentNameRule.js +8 -1
  85. package/dist/rules/issues/formulas/duplicateFormulaArgumentNameRule.js.map +1 -1
  86. package/dist/rules/issues/formulas/legacyFormulaRule.js +12 -5
  87. package/dist/rules/issues/formulas/legacyFormulaRule.js.map +1 -1
  88. package/dist/rules/issues/formulas/noReferenceComponentFormulaRule.js +14 -4
  89. package/dist/rules/issues/formulas/noReferenceComponentFormulaRule.js.map +1 -1
  90. package/dist/rules/issues/formulas/noReferenceProjectFormulaRule.js +8 -1
  91. package/dist/rules/issues/formulas/noReferenceProjectFormulaRule.js.map +1 -1
  92. package/dist/rules/issues/formulas/unknownComponentFormulaInputRule.js +8 -1
  93. package/dist/rules/issues/formulas/unknownComponentFormulaInputRule.js.map +1 -1
  94. package/dist/rules/issues/formulas/unknownFormulaRule.js +8 -1
  95. package/dist/rules/issues/formulas/unknownFormulaRule.js.map +1 -1
  96. package/dist/rules/issues/formulas/unknownProjectFormulaInputRule.js +8 -1
  97. package/dist/rules/issues/formulas/unknownProjectFormulaInputRule.js.map +1 -1
  98. package/dist/rules/issues/formulas/unknownProjectFormulaRule.js +8 -1
  99. package/dist/rules/issues/formulas/unknownProjectFormulaRule.js.map +1 -1
  100. package/dist/rules/issues/formulas/unknownRepeatIndexFormulaRule.js +14 -2
  101. package/dist/rules/issues/formulas/unknownRepeatIndexFormulaRule.js.map +1 -1
  102. package/dist/rules/issues/formulas/unknownRepeatItemFormulaRule.js +14 -2
  103. package/dist/rules/issues/formulas/unknownRepeatItemFormulaRule.js.map +1 -1
  104. package/dist/rules/issues/logic/noStaticNodeCondition.js +15 -3
  105. package/dist/rules/issues/logic/noStaticNodeCondition.js.map +1 -1
  106. package/dist/rules/issues/logic/noUnnecessaryConditionFalsy.js +7 -1
  107. package/dist/rules/issues/logic/noUnnecessaryConditionFalsy.js.map +1 -1
  108. package/dist/rules/issues/logic/noUnnecessaryConditionTruthy.js +7 -1
  109. package/dist/rules/issues/logic/noUnnecessaryConditionTruthy.js.map +1 -1
  110. package/dist/rules/issues/miscellaneous/createStaticSizeConstraintRule.d.ts +5 -0
  111. package/dist/rules/issues/miscellaneous/createStaticSizeConstraintRule.js +74 -0
  112. package/dist/rules/issues/miscellaneous/createStaticSizeConstraintRule.js.map +1 -0
  113. package/dist/rules/issues/miscellaneous/miscRules.index.d.ts +3 -0
  114. package/dist/rules/issues/miscellaneous/miscRules.index.js +10 -1
  115. package/dist/rules/issues/miscellaneous/miscRules.index.js.map +1 -1
  116. package/dist/rules/issues/miscellaneous/noReferenceNodeRule.js +9 -1
  117. package/dist/rules/issues/miscellaneous/noReferenceNodeRule.js.map +1 -1
  118. package/dist/rules/issues/miscellaneous/requireExtensionRule.js +7 -1
  119. package/dist/rules/issues/miscellaneous/requireExtensionRule.js.map +1 -1
  120. package/dist/rules/issues/miscellaneous/unknownCookieRule.js +8 -1
  121. package/dist/rules/issues/miscellaneous/unknownCookieRule.js.map +1 -1
  122. package/dist/rules/issues/routing/duplicateRouteRule.js +16 -6
  123. package/dist/rules/issues/routing/duplicateRouteRule.js.map +1 -1
  124. package/dist/rules/issues/routing/duplicateUrlParameterRule.js +16 -2
  125. package/dist/rules/issues/routing/duplicateUrlParameterRule.js.map +1 -1
  126. package/dist/rules/issues/routing/unknownSetUrlParameterRule.js +8 -1
  127. package/dist/rules/issues/routing/unknownSetUrlParameterRule.js.map +1 -1
  128. package/dist/rules/issues/routing/unknownSetUrlParametersRule.js +8 -1
  129. package/dist/rules/issues/routing/unknownSetUrlParametersRule.js.map +1 -1
  130. package/dist/rules/issues/routing/unknownUrlParameterRule.js +8 -1
  131. package/dist/rules/issues/routing/unknownUrlParameterRule.js.map +1 -1
  132. package/dist/rules/issues/slots/unknownComponentSlotRule.js +9 -2
  133. package/dist/rules/issues/slots/unknownComponentSlotRule.js.map +1 -1
  134. package/dist/rules/issues/style/invalidStyleSyntaxRule.js +9 -1
  135. package/dist/rules/issues/style/invalidStyleSyntaxRule.js.map +1 -1
  136. package/dist/rules/issues/style/legacyStyleVariableRule.js +9 -3
  137. package/dist/rules/issues/style/legacyStyleVariableRule.js.map +1 -1
  138. package/dist/rules/issues/style/legacyThemeRule.js +7 -1
  139. package/dist/rules/issues/style/legacyThemeRule.js.map +1 -1
  140. package/dist/rules/issues/style/noReferenceGlobalCSSVariable.js +8 -1
  141. package/dist/rules/issues/style/noReferenceGlobalCSSVariable.js.map +1 -1
  142. package/dist/rules/issues/style/unknownCSSVariable.js +43 -45
  143. package/dist/rules/issues/style/unknownCSSVariable.js.map +1 -1
  144. package/dist/rules/issues/style/unknownClassnameRule.js +8 -1
  145. package/dist/rules/issues/style/unknownClassnameRule.js.map +1 -1
  146. package/dist/rules/issues/variables/noReferenceVariableRule.js +8 -1
  147. package/dist/rules/issues/variables/noReferenceVariableRule.js.map +1 -1
  148. package/dist/rules/issues/variables/unknownVariableRule.js +8 -1
  149. package/dist/rules/issues/variables/unknownVariableRule.js.map +1 -1
  150. package/dist/rules/issues/variables/unknownVariableSetterRule.js +8 -1
  151. package/dist/rules/issues/variables/unknownVariableSetterRule.js.map +1 -1
  152. package/dist/rules/issues/workflows/duplicateWorkflowParameterRule.js +8 -1
  153. package/dist/rules/issues/workflows/duplicateWorkflowParameterRule.js.map +1 -1
  154. package/dist/rules/issues/workflows/noPostNavigateAction.js +8 -1
  155. package/dist/rules/issues/workflows/noPostNavigateAction.js.map +1 -1
  156. package/dist/rules/issues/workflows/noReferenceComponentWorkflowRule.js +8 -1
  157. package/dist/rules/issues/workflows/noReferenceComponentWorkflowRule.js.map +1 -1
  158. package/dist/rules/issues/workflows/unknownContextProviderWorkflowRule.js +10 -3
  159. package/dist/rules/issues/workflows/unknownContextProviderWorkflowRule.js.map +1 -1
  160. package/dist/rules/issues/workflows/unknownContextWorkflowRule.js +10 -3
  161. package/dist/rules/issues/workflows/unknownContextWorkflowRule.js.map +1 -1
  162. package/dist/rules/issues/workflows/unknownTriggerWorkflowParameterRule.js +9 -2
  163. package/dist/rules/issues/workflows/unknownTriggerWorkflowParameterRule.js.map +1 -1
  164. package/dist/rules/issues/workflows/unknownTriggerWorkflowRule.js +7 -1
  165. package/dist/rules/issues/workflows/unknownTriggerWorkflowRule.js.map +1 -1
  166. package/dist/rules/issues/workflows/unknownWorkflowParameterRule.js +9 -2
  167. package/dist/rules/issues/workflows/unknownWorkflowParameterRule.js.map +1 -1
  168. package/dist/searchProject.js +2 -1
  169. package/dist/searchProject.js.map +1 -1
  170. package/dist/types.d.ts +13 -2
  171. package/package.json +3 -3
  172. package/src/rules/issues/actions/actionRules.index.ts +5 -0
  173. package/src/rules/issues/actions/createActionNameRule.ts +10 -1
  174. package/src/rules/issues/actions/duplicateActionArgumentNameRule.ts +8 -1
  175. package/src/rules/issues/actions/legacyActionRule.ts +10 -3
  176. package/src/rules/issues/actions/noReferenceProjectActionRule.ts +8 -1
  177. package/src/rules/issues/actions/unknownActionArgumentRule.ts +18 -9
  178. package/src/rules/issues/actions/unknownActionEventRule.ts +8 -4
  179. package/src/rules/issues/actions/unknownProjectActionRule.ts +8 -1
  180. package/src/rules/issues/apis/invalidApiParserModeRule.ts +8 -1
  181. package/src/rules/issues/apis/invalidApiProxyBodySettingRule.ts +8 -1
  182. package/src/rules/issues/apis/invalidApiProxyCookieSettingRule.ts +8 -1
  183. package/src/rules/issues/apis/legacyApiRule.ts +8 -1
  184. package/src/rules/issues/apis/noReferenceApiInputRule.ts +8 -1
  185. package/src/rules/issues/apis/noReferenceApiRule.ts +8 -1
  186. package/src/rules/issues/apis/noReferenceApiServiceRule.ts +9 -1
  187. package/src/rules/issues/apis/unknownApiInputRule.ts +8 -1
  188. package/src/rules/issues/apis/unknownApiRule.ts +16 -2
  189. package/src/rules/issues/apis/unknownApiServiceRule.ts +9 -5
  190. package/src/rules/issues/apis/unknownFetchInputRule.ts +9 -1
  191. package/src/rules/issues/attributes/noReferenceAttributeInInstanceRule.ts +9 -2
  192. package/src/rules/issues/attributes/noReferenceAttributeRule.ts +8 -1
  193. package/src/rules/issues/attributes/unknownAttributeRule.ts +8 -1
  194. package/src/rules/issues/attributes/unknownComponentAttributeRule.ts +9 -3
  195. package/src/rules/issues/components/invalidComponentStructureRule.ts +9 -5
  196. package/src/rules/issues/components/noReferenceComponentRule.ts +9 -1
  197. package/src/rules/issues/components/unknownComponentRule.ts +8 -1
  198. package/src/rules/issues/context/noContextConsumersRule.ts +8 -1
  199. package/src/rules/issues/context/unknownContextFormulaRule.ts +10 -3
  200. package/src/rules/issues/context/unknownContextProviderFormulaRule.ts +10 -3
  201. package/src/rules/issues/context/unknownContextProviderRule.ts +8 -1
  202. package/src/rules/issues/dom/createRequiredDirectChildRule.ts +11 -4
  203. package/src/rules/issues/dom/createRequiredDirectParentRule.ts +11 -4
  204. package/src/rules/issues/dom/createRequiredElementAttributeRule.ts +8 -1
  205. package/src/rules/issues/dom/createRequiredMetaTagRule.ts +14 -1
  206. package/src/rules/issues/dom/elementWithoutInteractiveContentRule.ts +26 -3
  207. package/src/rules/issues/dom/imageWithoutDimensionRule.ts +8 -1
  208. package/src/rules/issues/dom/nonEmptyVoidElementRule.ts +8 -1
  209. package/src/rules/issues/events/duplicateEventTriggerRule.ts +8 -1
  210. package/src/rules/issues/events/noReferenceEventRule.ts +9 -1
  211. package/src/rules/issues/events/unknownEventRule.ts +8 -1
  212. package/src/rules/issues/events/unknownTriggerEventRule.ts +8 -1
  213. package/src/rules/issues/formulas/duplicateFormulaArgumentNameRule.ts +8 -1
  214. package/src/rules/issues/formulas/legacyFormulaRule.ts +12 -7
  215. package/src/rules/issues/formulas/noReferenceComponentFormulaRule.ts +12 -5
  216. package/src/rules/issues/formulas/noReferenceProjectFormulaRule.ts +8 -1
  217. package/src/rules/issues/formulas/unknownComponentFormulaInputRule.ts +8 -1
  218. package/src/rules/issues/formulas/unknownFormulaRule.ts +8 -1
  219. package/src/rules/issues/formulas/unknownProjectFormulaInputRule.ts +8 -1
  220. package/src/rules/issues/formulas/unknownProjectFormulaRule.ts +8 -1
  221. package/src/rules/issues/formulas/unknownRepeatIndexFormulaRule.ts +14 -2
  222. package/src/rules/issues/formulas/unknownRepeatItemFormulaRule.ts +14 -2
  223. package/src/rules/issues/logic/noStaticNodeCondition.ts +15 -4
  224. package/src/rules/issues/logic/noUnnecessaryConditionFalsy.ts +8 -1
  225. package/src/rules/issues/logic/noUnnecessaryConditionTruthy.ts +8 -1
  226. package/src/rules/issues/miscellaneous/createStaticSizeConstraintRule.test.ts +153 -0
  227. package/src/rules/issues/miscellaneous/createStaticSizeConstraintRule.ts +90 -0
  228. package/src/rules/issues/miscellaneous/miscRules.index.ts +10 -1
  229. package/src/rules/issues/miscellaneous/noReferenceNodeRule.ts +9 -1
  230. package/src/rules/issues/miscellaneous/requireExtensionRule.ts +8 -1
  231. package/src/rules/issues/miscellaneous/unknownCookieRule.ts +8 -1
  232. package/src/rules/issues/routing/duplicateRouteRule.ts +18 -6
  233. package/src/rules/issues/routing/duplicateUrlParameterRule.ts +16 -2
  234. package/src/rules/issues/routing/unknownSetUrlParameterRule.ts +8 -1
  235. package/src/rules/issues/routing/unknownSetUrlParametersRule.ts +8 -1
  236. package/src/rules/issues/routing/unknownUrlParameterRule.ts +8 -1
  237. package/src/rules/issues/slots/unknownComponentSlotRule.ts +9 -2
  238. package/src/rules/issues/style/invalidStyleSyntaxRule.ts +9 -1
  239. package/src/rules/issues/style/legacyStyleVariableRule.ts +9 -3
  240. package/src/rules/issues/style/legacyThemeRule.ts +7 -1
  241. package/src/rules/issues/style/noReferenceGlobalCSSVariable.ts +8 -1
  242. package/src/rules/issues/style/unknownCSSVariable.test.ts +45 -37
  243. package/src/rules/issues/style/unknownCSSVariable.ts +49 -52
  244. package/src/rules/issues/style/unknownClassnameRule.ts +8 -1
  245. package/src/rules/issues/variables/noReferenceVariableRule.ts +8 -1
  246. package/src/rules/issues/variables/unknownVariableRule.ts +8 -1
  247. package/src/rules/issues/variables/unknownVariableSetterRule.ts +8 -1
  248. package/src/rules/issues/workflows/duplicateWorkflowParameterRule.ts +8 -1
  249. package/src/rules/issues/workflows/noPostNavigateAction.ts +8 -1
  250. package/src/rules/issues/workflows/noReferenceComponentWorkflowRule.ts +8 -1
  251. package/src/rules/issues/workflows/unknownContextProviderWorkflowRule.ts +10 -3
  252. package/src/rules/issues/workflows/unknownContextWorkflowRule.ts +10 -3
  253. package/src/rules/issues/workflows/unknownTriggerWorkflowParameterRule.ts +9 -2
  254. package/src/rules/issues/workflows/unknownTriggerWorkflowRule.ts +7 -1
  255. package/src/rules/issues/workflows/unknownWorkflowParameterRule.ts +9 -2
  256. package/src/searchProject.ts +2 -1
  257. package/src/types.ts +22 -10
@@ -23,14 +23,19 @@ export const legacyFormulaRule: Rule<
23
23
  ) {
24
24
  return
25
25
  }
26
- report(
27
- data.path,
28
- { name: data.value.name },
26
+ report({
27
+ path: data.path,
28
+ info: {
29
+ title: 'Legacy formula',
30
+ description: `**${data.value.name}** is deprecated. Replace it with the corresponding core formula.`,
31
+ },
32
+ details: { name: data.value.name },
29
33
  // The TYPE and BOOLEAN formulas cannot be autofixed since the logic has changed between the 2 implementations
30
- data.value.name !== 'TYPE' && data.value.name !== 'BOOLEAN'
31
- ? ['replace-legacy-formula']
32
- : undefined,
33
- )
34
+ fixes:
35
+ data.value.name !== 'TYPE' && data.value.name !== 'BOOLEAN'
36
+ ? ['replace-legacy-formula']
37
+ : undefined,
38
+ })
34
39
  },
35
40
  fixes: {
36
41
  'replace-legacy-formula': replaceLegacyFormula,
@@ -75,14 +75,21 @@ export const noReferenceComponentFormulaRule: Rule<{
75
75
  }
76
76
  }
77
77
  }
78
- report(
79
- args.path,
80
- {
78
+ report({
79
+ path: args.path,
80
+ info: {
81
+ title: 'Unused component formula',
82
+ description:
83
+ contextSubscribers.length > 0
84
+ ? `**${value.name}** is never used but is subscribed to by: *${contextSubscribers.join('*, *')}*. Consider removing it and clean up the unused context subscriptions.`
85
+ : `**${value.name}** is never used. Consider removing it.`,
86
+ },
87
+ details: {
81
88
  contextSubscribers,
82
89
  name: value.name,
83
90
  },
84
- ['delete-component-formula'],
85
- )
91
+ fixes: ['delete-component-formula'],
92
+ })
86
93
  },
87
94
  fixes: {
88
95
  'delete-component-formula': removeFromPathFix,
@@ -85,7 +85,14 @@ export const noReferenceProjectFormulaRule: Rule<void> = {
85
85
  return
86
86
  }
87
87
  }
88
- report(path, undefined, ['delete-project-formula'])
88
+ report({
89
+ path,
90
+ info: {
91
+ title: 'Unused global formula',
92
+ description: `Global formula is never used by any formula. Consider removing it.`,
93
+ },
94
+ fixes: ['delete-project-formula'],
95
+ })
89
96
  },
90
97
  fixes: {
91
98
  'delete-project-formula': removeFromPathFix,
@@ -32,7 +32,14 @@ export const unknownComponentFormulaInputRule: Rule<{
32
32
  const args = formula.arguments ?? []
33
33
  const argName = value.path[1]
34
34
  if (!isDefined(argName) || !args.some((arg) => arg.name === argName)) {
35
- report(path, { name: argName })
35
+ report({
36
+ path,
37
+ info: {
38
+ title: 'Unknown component formula input',
39
+ description: `The formula input ${argName ?? ''} doesn't exist as an input for this formula.`,
40
+ },
41
+ details: { name: argName },
42
+ })
36
43
  }
37
44
  },
38
45
  }
@@ -15,7 +15,14 @@ export const unknownFormulaRule: Rule<{
15
15
  const [, componentName] = path
16
16
  const component = files.components[componentName]
17
17
  if (!component?.formulas?.[value.name]) {
18
- report(path, { name: value.name })
18
+ report({
19
+ path,
20
+ info: {
21
+ title: 'Unknown formula',
22
+ description: `**${value.name}** does not exist. Using an unknown formula will always return *Null*`,
23
+ },
24
+ details: { name: value.name },
25
+ })
19
26
  }
20
27
  },
21
28
  }
@@ -26,7 +26,14 @@ export const unknownProjectFormulaInputRule: Rule<{
26
26
  const args = formula.arguments ?? []
27
27
  const argName = value.path[1]
28
28
  if (!isDefined(argName) || !args.some((arg) => arg.name === argName)) {
29
- report(path, { name: argName })
29
+ report({
30
+ path,
31
+ info: {
32
+ title: 'Unknown project formula input',
33
+ description: `The formula input ${argName ?? ''} doesn't exist as an input for this formula.`,
34
+ },
35
+ details: { name: argName },
36
+ })
30
37
  }
31
38
  },
32
39
  }
@@ -18,6 +18,13 @@ export const unknownProjectFormulaRule: Rule<{ name: string }> = {
18
18
  if (formula) {
19
19
  return
20
20
  }
21
- report(path, { name: value.name })
21
+ report({
22
+ path,
23
+ info: {
24
+ title: 'Unknown global formula',
25
+ description: `**${value.name}** does not exist. Using an unknown formula will always return *Null*`,
26
+ },
27
+ details: { name: value.name },
28
+ })
22
29
  },
23
30
  }
@@ -16,7 +16,13 @@ export const unknownRepeatIndexFormulaRule: Rule = {
16
16
  }
17
17
  if (path[0] !== 'components' || path[2] !== 'nodes') {
18
18
  // Any use outside of a component node is invalid
19
- return report(path)
19
+ return report({
20
+ path,
21
+ info: {
22
+ title: 'Unknown repeat index reference',
23
+ description: `The formula references a repeat index, but no repeat formula exist in any of its parent elements.`,
24
+ },
25
+ })
20
26
  }
21
27
  const [_components, componentName, _nodes, nodeId] = path as string[]
22
28
  const component = files.components[componentName]
@@ -44,7 +50,13 @@ export const unknownRepeatIndexFormulaRule: Rule = {
44
50
  }
45
51
  const parentWithRepeat = findParentWithRepeat([nodeId, node])
46
52
  if (!parentWithRepeat) {
47
- report(path)
53
+ report({
54
+ path,
55
+ info: {
56
+ title: 'Unknown repeat index reference',
57
+ description: `The formula references a repeat index, but no repeat formula exist in any of its parent elements.`,
58
+ },
59
+ })
48
60
  }
49
61
  },
50
62
  }
@@ -17,7 +17,13 @@ export const unknownRepeatItemFormulaRule: Rule = {
17
17
  if (path[0] !== 'components' || path[2] !== 'nodes') {
18
18
  // Any use outside of a component node is invalid
19
19
  // For instance in global formulas or in workflows
20
- return report(path)
20
+ return report({
21
+ path,
22
+ info: {
23
+ title: 'Unknown repeat item reference',
24
+ description: `The formula references a repeat item, but no repeat formula exist in any of its parent elements.`,
25
+ },
26
+ })
21
27
  }
22
28
  const [_components, componentName, _nodes, nodeId] = path as string[]
23
29
  const component = files.components[componentName]
@@ -45,7 +51,13 @@ export const unknownRepeatItemFormulaRule: Rule = {
45
51
  }
46
52
  const parentWithRepeat = findParentWithRepeat([nodeId, node])
47
53
  if (!parentWithRepeat) {
48
- report(path)
54
+ report({
55
+ path,
56
+ info: {
57
+ title: 'Unknown repeat item reference',
58
+ description: `The formula references a repeat item, but no repeat formula exist in any of its parent elements.`,
59
+ },
60
+ })
49
61
  }
50
62
  },
51
63
  }
@@ -28,13 +28,24 @@ export const noStaticNodeCondition: Rule<{
28
28
  if (isStatic) {
29
29
  // - if truthy: "Condition is always true, you can safely remove the condition as it will always be rendered."
30
30
  // - if falsy: "Condition is always false, you can safely remove the entire node as it will never be rendered."
31
- report(
31
+ report({
32
32
  path,
33
- {
33
+ info: {
34
+ title:
35
+ Boolean(result) === true
36
+ ? `Unnecessary show/hide, value is always truthy`
37
+ : `Unnecessary show/hide, value is always falsy`,
38
+ description:
39
+ Boolean(result) === true
40
+ ? `Condition always evaluates to **show** the element. Consider removing the show/hide formula.`
41
+ : `Condition always evaluates to **hide** the element. Consider removing the element node.`,
42
+ },
43
+ details: {
34
44
  result,
35
45
  },
36
- Boolean(result) === true ? ['remove-condition'] : ['remove-node'],
37
- )
46
+ fixes:
47
+ Boolean(result) === true ? ['remove-condition'] : ['remove-node'],
48
+ })
38
49
  }
39
50
  },
40
51
  fixes: {
@@ -16,7 +16,14 @@ export const noUnnecessaryConditionFalsy: Rule = {
16
16
  return isStatic && Boolean(result) === false
17
17
  })
18
18
  ) {
19
- report(path)
19
+ report({
20
+ path,
21
+ info: {
22
+ title: 'Unnecessary condition',
23
+ description:
24
+ '**And condition** is always falsy. Consider replacing it with a single *false* node.',
25
+ },
26
+ })
20
27
  }
21
28
  },
22
29
  }
@@ -21,7 +21,14 @@ export const noUnnecessaryConditionTruthy: Rule = {
21
21
  return isStatic && Boolean(result) === true
22
22
  })
23
23
  ) {
24
- report(path)
24
+ report({
25
+ path,
26
+ info: {
27
+ title: 'Unnecessary condition',
28
+ description:
29
+ '**Or condition** is always truthy. Consider replacing it with a single *true* node.',
30
+ },
31
+ })
25
32
  }
26
33
  },
27
34
  }
@@ -0,0 +1,153 @@
1
+ import { valueFormula } from '@nordcraft/core/dist/formula/formulaUtils'
2
+ import { describe, expect, test } from 'bun:test'
3
+ import { searchProject } from '../../../searchProject'
4
+ import { createStaticSizeConstraintRule } from './createStaticSizeConstraintRule'
5
+
6
+ describe('createStaticSizeConstraintRule', () => {
7
+ test('should calculate element size correctly for SVGs', () => {
8
+ const problems = Array.from(
9
+ searchProject({
10
+ files: {
11
+ formulas: {},
12
+ components: {
13
+ test: {
14
+ name: 'test',
15
+ nodes: {
16
+ root: {
17
+ type: 'element',
18
+ attrs: {},
19
+ classes: {},
20
+ events: {},
21
+ tag: 'img',
22
+ children: ['svg'],
23
+ style: {},
24
+ },
25
+ svg: {
26
+ type: 'element',
27
+ attrs: {},
28
+ classes: {},
29
+ events: {},
30
+ tag: 'svg',
31
+ children: ['path'],
32
+ style: {},
33
+ },
34
+ path: {
35
+ type: 'element',
36
+ attrs: {},
37
+ classes: {},
38
+ events: {},
39
+ tag: 'path',
40
+ children: [],
41
+ style: {},
42
+ },
43
+ },
44
+ formulas: {},
45
+ apis: {},
46
+ attributes: {},
47
+ variables: {},
48
+ },
49
+ },
50
+ },
51
+ rules: [createStaticSizeConstraintRule('svg', 1)],
52
+ }),
53
+ )
54
+
55
+ expect(problems).toHaveLength(1)
56
+ expect(problems[0].code).toBe('size constraint')
57
+ expect(problems[0].path).toEqual(['components', 'test', 'nodes', 'svg'])
58
+ const expectedSize = new Blob(['<svg><path></path></svg>']).size
59
+ expect(problems[0].details.size).toEqual(expectedSize)
60
+ })
61
+ test('should calculate element size correctly for imgs', () => {
62
+ const problems = Array.from(
63
+ searchProject({
64
+ files: {
65
+ formulas: {},
66
+ components: {
67
+ test: {
68
+ name: 'test',
69
+ nodes: {
70
+ root: {
71
+ type: 'element',
72
+ attrs: {
73
+ src: valueFormula(
74
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAADMElEQVR4nOzVwQnAIBQFQYXff81RUkQCOyDj1YOPnbXWPmeTRef+/3O/OyBjzh3CD95BfqICMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMO0TAAD//2Anhf4QtqobAAAAAElFTkSuQmCC',
75
+ ),
76
+ },
77
+ classes: {},
78
+ events: {},
79
+ tag: 'img',
80
+ children: [],
81
+ style: {},
82
+ },
83
+ },
84
+ formulas: {},
85
+ apis: {},
86
+ attributes: {},
87
+ variables: {},
88
+ },
89
+ },
90
+ },
91
+ rules: [createStaticSizeConstraintRule('img', 1)],
92
+ }),
93
+ )
94
+
95
+ expect(problems).toHaveLength(1)
96
+ expect(problems[0].code).toBe('size constraint')
97
+ expect(problems[0].path).toEqual(['components', 'test', 'nodes', 'root'])
98
+ const expectedSize = new Blob([
99
+ '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAADMElEQVR4nOzVwQnAIBQFQYXff81RUkQCOyDj1YOPnbXWPmeTRef+/3O/OyBjzh3CD95BfqICMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMK0CMO0TAAD//2Anhf4QtqobAAAAAElFTkSuQmCC" />',
100
+ ]).size
101
+ expect(problems[0].details.size).toEqual(expectedSize)
102
+ })
103
+ test('should ignore elements that are not too large', () => {
104
+ const problems = Array.from(
105
+ searchProject({
106
+ files: {
107
+ formulas: {},
108
+ components: {
109
+ test: {
110
+ name: 'test',
111
+ nodes: {
112
+ root: {
113
+ type: 'element',
114
+ attrs: {},
115
+ classes: {},
116
+ events: {},
117
+ tag: 'img',
118
+ children: ['svg'],
119
+ style: {},
120
+ },
121
+ svg: {
122
+ type: 'element',
123
+ attrs: {},
124
+ classes: {},
125
+ events: {},
126
+ tag: 'svg',
127
+ children: ['path'],
128
+ style: {},
129
+ },
130
+ path: {
131
+ type: 'element',
132
+ attrs: {},
133
+ classes: {},
134
+ events: {},
135
+ tag: 'path',
136
+ children: [],
137
+ style: {},
138
+ },
139
+ },
140
+ formulas: {},
141
+ apis: {},
142
+ attributes: {},
143
+ variables: {},
144
+ },
145
+ },
146
+ },
147
+ rules: [createStaticSizeConstraintRule('svg', 100)],
148
+ }),
149
+ )
150
+
151
+ expect(problems).toHaveLength(0)
152
+ })
153
+ })
@@ -0,0 +1,90 @@
1
+ import type { NodeModel } from '@nordcraft/core/dist/component/component.types'
2
+ import { isDefined } from '@nordcraft/core/dist/utils/util'
3
+ import { VOID_HTML_ELEMENTS } from '@nordcraft/ssr/dist/const'
4
+ import type { Level, Rule } from '../../../types'
5
+
6
+ export function createStaticSizeConstraintRule(
7
+ tag: string,
8
+ maxSize: number,
9
+ level: Level = 'info',
10
+ ): Rule<{
11
+ tag: string
12
+ size: number
13
+ }> {
14
+ return {
15
+ code: 'size constraint',
16
+ category: 'Performance',
17
+ level: level,
18
+ visit: (report, args) => {
19
+ if (
20
+ args.nodeType === 'component-node' &&
21
+ args.value.type === 'element' &&
22
+ args.value.tag === tag
23
+ ) {
24
+ let size = 0
25
+ const component = args.component
26
+ const evaluateElement = (element?: NodeModel): string => {
27
+ if (
28
+ !element ||
29
+ ['element', 'text', 'slot', 'component'].includes(element.type) ===
30
+ false
31
+ ) {
32
+ return ''
33
+ }
34
+ const children = (element.children ?? []).map((child) =>
35
+ evaluateElement(component.nodes?.[child]),
36
+ )
37
+ const tag = element.type === 'element' ? element.tag : 'span'
38
+ const attributes: Record<string, string> = {}
39
+ if (element.type === 'element' || element.type === 'component') {
40
+ Object.entries(element.attrs ?? {}).forEach(([key, value]) => {
41
+ if (value?.type === 'value' && isDefined(value.value)) {
42
+ attributes[key] = String(value.value)
43
+ }
44
+ })
45
+ }
46
+ const attributeString = Object.entries(attributes)
47
+ .map(([key, value]) => `${key}="${value}"`)
48
+ .join(' ')
49
+ if (VOID_HTML_ELEMENTS.includes(tag)) {
50
+ return attributeString
51
+ ? `<${tag} ${attributeString} />`
52
+ : `<${tag} />`
53
+ } else {
54
+ const openingTag = attributeString
55
+ ? `<${tag} ${attributeString}>`
56
+ : `<${tag}>`
57
+ return `${openingTag}${children.join('')}</${tag}>`
58
+ }
59
+ }
60
+ const staticElement = evaluateElement(args.value)
61
+ size = new Blob([staticElement]).size
62
+ if (size > maxSize) {
63
+ report({
64
+ path: args.path,
65
+ info: {
66
+ title: `Element size exceeds the suggested maximum allowed size of ${sizeFormatter(maxSize)}`,
67
+ description: `The <${tag}> element has a size of ${sizeFormatter(size)}, which exceeds the suggested limit of ${sizeFormatter(maxSize)}. Consider simplifying the content or structure of this element to reduce its size and improve performance.`,
68
+ },
69
+ details: { tag, size },
70
+ })
71
+ }
72
+ }
73
+ },
74
+ }
75
+ }
76
+
77
+ const sizeFormatter = (sizeInBytes: number) => {
78
+ const units = ['byte', 'KB', 'MB', 'GB', 'TB']
79
+ let unitIndex = 0
80
+ let size = sizeInBytes
81
+
82
+ while (size >= 1024 && unitIndex < units.length - 1) {
83
+ size /= 1024
84
+ unitIndex++
85
+ }
86
+ const unit = unitIndex === 0 && size !== 1 ? 'bytes' : units[unitIndex]
87
+ return `${new Intl.NumberFormat(undefined, {
88
+ maximumFractionDigits: unitIndex === 0 ? 0 : 2,
89
+ }).format(size)} ${unit}`
90
+ }
@@ -1,5 +1,14 @@
1
+ import { createStaticSizeConstraintRule } from './createStaticSizeConstraintRule'
1
2
  import { noReferenceNodeRule } from './noReferenceNodeRule'
2
3
  import { requireExtensionRule } from './requireExtensionRule'
3
4
  import { unknownCookieRule } from './unknownCookieRule'
4
5
 
5
- export default [noReferenceNodeRule, requireExtensionRule, unknownCookieRule]
6
+ export default [
7
+ noReferenceNodeRule,
8
+ requireExtensionRule,
9
+ unknownCookieRule,
10
+ // 100 KB is a large SVG
11
+ createStaticSizeConstraintRule('svg', 100 * 1024),
12
+ // 50 KB is a large img element (with potential base64 encoded image)
13
+ createStaticSizeConstraintRule('img', 50 * 1024),
14
+ ]
@@ -25,7 +25,15 @@ export const noReferenceNodeRule: Rule<{ node: string }> = {
25
25
  )
26
26
 
27
27
  if (nodeId !== 'root' && !referencedNodesInComponent.has(nodeId)) {
28
- report(path, { node: nodeId }, ['delete-orphan-node'])
28
+ report({
29
+ path,
30
+ info: {
31
+ title: `Orphan node/element was found`,
32
+ description: `A node is declared, but it is not included in any other element, including the root element. This node can safely be removed.`,
33
+ },
34
+ details: { node: nodeId },
35
+ fixes: ['delete-orphan-node'],
36
+ })
29
37
  }
30
38
  },
31
39
  fixes: {
@@ -16,6 +16,13 @@ export const requireExtensionRule: Rule<{
16
16
  ) {
17
17
  return
18
18
  }
19
- report(path)
19
+ report({
20
+ path,
21
+ info: {
22
+ title: 'Browser plugin recommended',
23
+ description:
24
+ '**Toddle browser plugin** is recommended for working with cookies. [Install extension](https://chromewebstore.google.com/detail/toddle/hfhgjncckomifajhndceigiaiojhlllp?hl=en)',
25
+ },
26
+ })
20
27
  },
21
28
  }
@@ -27,7 +27,14 @@ export const unknownCookieRule: Rule<{
27
27
  }
28
28
  const cookie = state.cookiesAvailable?.find((c) => c.name === formula.value)
29
29
  if (!cookie) {
30
- report(path, { name: formula.value })
30
+ report({
31
+ path,
32
+ info: {
33
+ title: 'Unknown cookie',
34
+ description: `**${formula.value}** is not found in the browser extension cookies.`,
35
+ },
36
+ details: { name: formula.value },
37
+ })
31
38
  }
32
39
  },
33
40
  }
@@ -40,12 +40,24 @@ export const duplicateRouteRule: Rule<{
40
40
  })
41
41
  const match = allRoutes.get(getRouteKey(value.route?.path))
42
42
  if (match && match.length > 1) {
43
- report([...path, 'route', 'path'], {
44
- name: value.name,
45
- type: 'page',
46
- duplicates: match
47
- .filter((m) => m !== value.name)
48
- .map((name) => ({ name, type: 'page' })),
43
+ const duplicates = match
44
+ .filter((m) => m !== value.name)
45
+ .map((name) => ({ name, type: 'page' as const }))
46
+ report({
47
+ path: [...path, 'route', 'path'],
48
+ info: {
49
+ title: 'Duplicate route declaration',
50
+ description: `The page **${
51
+ value.name
52
+ }** has the same route/path declared as the pages/routes below. Route declarations must be unique.${duplicates
53
+ .map((d) => `\n- ${d.name} (${d.type})`)
54
+ .join('')}`,
55
+ },
56
+ details: {
57
+ name: value.name,
58
+ type: 'page',
59
+ duplicates,
60
+ },
49
61
  })
50
62
  }
51
63
  },
@@ -17,13 +17,27 @@ export const duplicateUrlParameterRule: Rule<{ name: string }> = {
17
17
  const pathNames = new Set<string>()
18
18
  value.route.path.forEach((p, i) => {
19
19
  if (pathNames.has(p.name)) {
20
- report([...path, 'route', 'path', i], { name: p.name })
20
+ report({
21
+ path: [...path, 'route', 'path', i],
22
+ info: {
23
+ title: 'Duplicate URL parameter',
24
+ description: `**${p.name}** appears multiple times in the path/query parameters. This may lead to unexpected behavior. Consider consolidating them into a single parameter or renaming them.`,
25
+ },
26
+ details: { name: p.name },
27
+ })
21
28
  }
22
29
  pathNames.add(p.name)
23
30
  })
24
31
  Object.keys(value.route.query).forEach((key) => {
25
32
  if (pathNames.has(key)) {
26
- report([...path, 'route', 'query', key], { name: key })
33
+ report({
34
+ path: [...path, 'route', 'query', key],
35
+ info: {
36
+ title: 'Duplicate URL parameter',
37
+ description: `**${key}** appears multiple times in the path/query parameters. This may lead to unexpected behavior. Consider consolidating them into a single parameter or renaming them.`,
38
+ },
39
+ details: { name: key },
40
+ })
27
41
  }
28
42
  })
29
43
  },
@@ -27,7 +27,14 @@ export const unknownSetUrlParameterRule: Rule<{
27
27
  )
28
28
  const parameterName = args.value.parameter
29
29
  if (!isValidParameter(parameterName)) {
30
- report(args.path, { name: parameterName })
30
+ report({
31
+ path: args.path,
32
+ info: {
33
+ title: 'Unknown URL parameter update',
34
+ description: `**${parameterName}** does not exist as a path- or query-parameter and cannot be set/updated.`,
35
+ },
36
+ details: { name: parameterName },
37
+ })
31
38
  }
32
39
  },
33
40
  }
@@ -27,7 +27,14 @@ export const unknownSetUrlParametersRule: Rule<{
27
27
  )
28
28
  for (const key of Object.keys(args.value.parameters ?? {})) {
29
29
  if (!isValidParameter(key)) {
30
- report([...args.path, 'parameters', key], { name: key })
30
+ report({
31
+ path: [...args.path, 'parameters', key],
32
+ info: {
33
+ title: 'Unknown URL parameter update',
34
+ description: `**${key}** does not exist as a path- or query-parameter and cannot be set/updated.`,
35
+ },
36
+ details: { name: key },
37
+ })
31
38
  }
32
39
  }
33
40
  },