@memberjunction/react-test-harness 5.37.0 → 5.38.0

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 (342) hide show
  1. package/README.md +30 -0
  2. package/dist/cli/index.js +5 -1
  3. package/dist/cli/index.js.map +1 -1
  4. package/dist/index.d.ts +1 -5
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +10 -5
  7. package/dist/index.js.map +1 -1
  8. package/dist/lib/browser-context.d.ts +43 -0
  9. package/dist/lib/browser-context.d.ts.map +1 -1
  10. package/dist/lib/browser-context.js +87 -12
  11. package/dist/lib/browser-context.js.map +1 -1
  12. package/dist/lib/component-runner.d.ts +11 -22
  13. package/dist/lib/component-runner.d.ts.map +1 -1
  14. package/dist/lib/component-runner.js +1 -1
  15. package/dist/lib/component-runner.js.map +1 -1
  16. package/dist/lib/linter-test-tool.js +1 -1
  17. package/dist/lib/linter-test-tool.js.map +1 -1
  18. package/dist/lib/test-harness.d.ts +10 -0
  19. package/dist/lib/test-harness.d.ts.map +1 -1
  20. package/dist/lib/test-harness.js +10 -0
  21. package/dist/lib/test-harness.js.map +1 -1
  22. package/package.json +14 -13
  23. package/dist/lib/component-linter.d.ts +0 -77
  24. package/dist/lib/component-linter.d.ts.map +0 -1
  25. package/dist/lib/component-linter.js +0 -1205
  26. package/dist/lib/component-linter.js.map +0 -1
  27. package/dist/lib/control-flow-analyzer.d.ts +0 -184
  28. package/dist/lib/control-flow-analyzer.d.ts.map +0 -1
  29. package/dist/lib/control-flow-analyzer.js +0 -798
  30. package/dist/lib/control-flow-analyzer.js.map +0 -1
  31. package/dist/lib/library-lint-cache.d.ts +0 -50
  32. package/dist/lib/library-lint-cache.d.ts.map +0 -1
  33. package/dist/lib/library-lint-cache.js +0 -173
  34. package/dist/lib/library-lint-cache.js.map +0 -1
  35. package/dist/lib/lint-rule.d.ts +0 -70
  36. package/dist/lib/lint-rule.d.ts.map +0 -1
  37. package/dist/lib/lint-rule.js +0 -41
  38. package/dist/lib/lint-rule.js.map +0 -1
  39. package/dist/lib/lint-utils.d.ts +0 -131
  40. package/dist/lib/lint-utils.d.ts.map +0 -1
  41. package/dist/lib/lint-utils.js +0 -358
  42. package/dist/lib/lint-utils.js.map +0 -1
  43. package/dist/lib/prop-value-extractor.d.ts +0 -147
  44. package/dist/lib/prop-value-extractor.d.ts.map +0 -1
  45. package/dist/lib/prop-value-extractor.js +0 -472
  46. package/dist/lib/prop-value-extractor.js.map +0 -1
  47. package/dist/lib/runtime-rules/ai-tools-availability-check.d.ts +0 -9
  48. package/dist/lib/runtime-rules/ai-tools-availability-check.d.ts.map +0 -1
  49. package/dist/lib/runtime-rules/ai-tools-availability-check.js +0 -223
  50. package/dist/lib/runtime-rules/ai-tools-availability-check.js.map +0 -1
  51. package/dist/lib/runtime-rules/callback-event-validation.d.ts +0 -22
  52. package/dist/lib/runtime-rules/callback-event-validation.d.ts.map +0 -1
  53. package/dist/lib/runtime-rules/callback-event-validation.js +0 -561
  54. package/dist/lib/runtime-rules/callback-event-validation.js.map +0 -1
  55. package/dist/lib/runtime-rules/chart-field-validation.d.ts +0 -10
  56. package/dist/lib/runtime-rules/chart-field-validation.d.ts.map +0 -1
  57. package/dist/lib/runtime-rules/chart-field-validation.js +0 -270
  58. package/dist/lib/runtime-rules/chart-field-validation.js.map +0 -1
  59. package/dist/lib/runtime-rules/child-component-prop-validation.d.ts +0 -10
  60. package/dist/lib/runtime-rules/child-component-prop-validation.d.ts.map +0 -1
  61. package/dist/lib/runtime-rules/child-component-prop-validation.js +0 -443
  62. package/dist/lib/runtime-rules/child-component-prop-validation.js.map +0 -1
  63. package/dist/lib/runtime-rules/component-name-mismatch.d.ts +0 -19
  64. package/dist/lib/runtime-rules/component-name-mismatch.d.ts.map +0 -1
  65. package/dist/lib/runtime-rules/component-name-mismatch.js +0 -82
  66. package/dist/lib/runtime-rules/component-name-mismatch.js.map +0 -1
  67. package/dist/lib/runtime-rules/component-not-in-dependencies.d.ts +0 -20
  68. package/dist/lib/runtime-rules/component-not-in-dependencies.d.ts.map +0 -1
  69. package/dist/lib/runtime-rules/component-not-in-dependencies.js +0 -92
  70. package/dist/lib/runtime-rules/component-not-in-dependencies.js.map +0 -1
  71. package/dist/lib/runtime-rules/component-props-validation.d.ts +0 -25
  72. package/dist/lib/runtime-rules/component-props-validation.d.ts.map +0 -1
  73. package/dist/lib/runtime-rules/component-props-validation.js +0 -228
  74. package/dist/lib/runtime-rules/component-props-validation.js.map +0 -1
  75. package/dist/lib/runtime-rules/component-usage-without-destructuring.d.ts +0 -20
  76. package/dist/lib/runtime-rules/component-usage-without-destructuring.d.ts.map +0 -1
  77. package/dist/lib/runtime-rules/component-usage-without-destructuring.js +0 -124
  78. package/dist/lib/runtime-rules/component-usage-without-destructuring.js.map +0 -1
  79. package/dist/lib/runtime-rules/data-result-validation.d.ts +0 -9
  80. package/dist/lib/runtime-rules/data-result-validation.d.ts.map +0 -1
  81. package/dist/lib/runtime-rules/data-result-validation.js +0 -763
  82. package/dist/lib/runtime-rules/data-result-validation.js.map +0 -1
  83. package/dist/lib/runtime-rules/datagrid-field-validation.d.ts +0 -10
  84. package/dist/lib/runtime-rules/datagrid-field-validation.d.ts.map +0 -1
  85. package/dist/lib/runtime-rules/datagrid-field-validation.js +0 -249
  86. package/dist/lib/runtime-rules/datagrid-field-validation.js.map +0 -1
  87. package/dist/lib/runtime-rules/dependency-shadowing.d.ts +0 -20
  88. package/dist/lib/runtime-rules/dependency-shadowing.d.ts.map +0 -1
  89. package/dist/lib/runtime-rules/dependency-shadowing.js +0 -147
  90. package/dist/lib/runtime-rules/dependency-shadowing.js.map +0 -1
  91. package/dist/lib/runtime-rules/entity-field-access-validation.d.ts +0 -12
  92. package/dist/lib/runtime-rules/entity-field-access-validation.d.ts.map +0 -1
  93. package/dist/lib/runtime-rules/entity-field-access-validation.js +0 -304
  94. package/dist/lib/runtime-rules/entity-field-access-validation.js.map +0 -1
  95. package/dist/lib/runtime-rules/event-parameter-validation.d.ts +0 -22
  96. package/dist/lib/runtime-rules/event-parameter-validation.d.ts.map +0 -1
  97. package/dist/lib/runtime-rules/event-parameter-validation.js +0 -406
  98. package/dist/lib/runtime-rules/event-parameter-validation.js.map +0 -1
  99. package/dist/lib/runtime-rules/index.d.ts +0 -61
  100. package/dist/lib/runtime-rules/index.d.ts.map +0 -1
  101. package/dist/lib/runtime-rules/index.js +0 -62
  102. package/dist/lib/runtime-rules/index.js.map +0 -1
  103. package/dist/lib/runtime-rules/library-variable-names.d.ts +0 -24
  104. package/dist/lib/runtime-rules/library-variable-names.d.ts.map +0 -1
  105. package/dist/lib/runtime-rules/library-variable-names.js +0 -88
  106. package/dist/lib/runtime-rules/library-variable-names.js.map +0 -1
  107. package/dist/lib/runtime-rules/no-child-implementation.d.ts +0 -18
  108. package/dist/lib/runtime-rules/no-child-implementation.d.ts.map +0 -1
  109. package/dist/lib/runtime-rules/no-child-implementation.js +0 -57
  110. package/dist/lib/runtime-rules/no-child-implementation.js.map +0 -1
  111. package/dist/lib/runtime-rules/no-data-prop.d.ts +0 -22
  112. package/dist/lib/runtime-rules/no-data-prop.d.ts.map +0 -1
  113. package/dist/lib/runtime-rules/no-data-prop.js +0 -111
  114. package/dist/lib/runtime-rules/no-data-prop.js.map +0 -1
  115. package/dist/lib/runtime-rules/no-export-statements.d.ts +0 -18
  116. package/dist/lib/runtime-rules/no-export-statements.d.ts.map +0 -1
  117. package/dist/lib/runtime-rules/no-export-statements.js +0 -143
  118. package/dist/lib/runtime-rules/no-export-statements.js.map +0 -1
  119. package/dist/lib/runtime-rules/no-iife-wrapper.d.ts +0 -18
  120. package/dist/lib/runtime-rules/no-iife-wrapper.d.ts.map +0 -1
  121. package/dist/lib/runtime-rules/no-iife-wrapper.js +0 -217
  122. package/dist/lib/runtime-rules/no-iife-wrapper.js.map +0 -1
  123. package/dist/lib/runtime-rules/no-import-statements.d.ts +0 -18
  124. package/dist/lib/runtime-rules/no-import-statements.d.ts.map +0 -1
  125. package/dist/lib/runtime-rules/no-import-statements.js +0 -65
  126. package/dist/lib/runtime-rules/no-import-statements.js.map +0 -1
  127. package/dist/lib/runtime-rules/no-react-destructuring.d.ts +0 -18
  128. package/dist/lib/runtime-rules/no-react-destructuring.d.ts.map +0 -1
  129. package/dist/lib/runtime-rules/no-react-destructuring.js +0 -60
  130. package/dist/lib/runtime-rules/no-react-destructuring.js.map +0 -1
  131. package/dist/lib/runtime-rules/no-require-statements.d.ts +0 -18
  132. package/dist/lib/runtime-rules/no-require-statements.d.ts.map +0 -1
  133. package/dist/lib/runtime-rules/no-require-statements.js +0 -109
  134. package/dist/lib/runtime-rules/no-require-statements.js.map +0 -1
  135. package/dist/lib/runtime-rules/no-return-component.d.ts +0 -18
  136. package/dist/lib/runtime-rules/no-return-component.d.ts.map +0 -1
  137. package/dist/lib/runtime-rules/no-return-component.js +0 -106
  138. package/dist/lib/runtime-rules/no-return-component.js.map +0 -1
  139. package/dist/lib/runtime-rules/no-unwrap-utility-libs.d.ts +0 -20
  140. package/dist/lib/runtime-rules/no-unwrap-utility-libs.d.ts.map +0 -1
  141. package/dist/lib/runtime-rules/no-unwrap-utility-libs.js +0 -75
  142. package/dist/lib/runtime-rules/no-unwrap-utility-libs.js.map +0 -1
  143. package/dist/lib/runtime-rules/no-use-reducer.d.ts +0 -19
  144. package/dist/lib/runtime-rules/no-use-reducer.d.ts.map +0 -1
  145. package/dist/lib/runtime-rules/no-use-reducer.js +0 -78
  146. package/dist/lib/runtime-rules/no-use-reducer.js.map +0 -1
  147. package/dist/lib/runtime-rules/no-window-access.d.ts +0 -23
  148. package/dist/lib/runtime-rules/no-window-access.d.ts.map +0 -1
  149. package/dist/lib/runtime-rules/no-window-access.js +0 -136
  150. package/dist/lib/runtime-rules/no-window-access.js.map +0 -1
  151. package/dist/lib/runtime-rules/noisy-settings-updates.d.ts +0 -18
  152. package/dist/lib/runtime-rules/noisy-settings-updates.d.ts.map +0 -1
  153. package/dist/lib/runtime-rules/noisy-settings-updates.js +0 -110
  154. package/dist/lib/runtime-rules/noisy-settings-updates.js.map +0 -1
  155. package/dist/lib/runtime-rules/overflow-hidden-on-layout-container.d.ts +0 -30
  156. package/dist/lib/runtime-rules/overflow-hidden-on-layout-container.d.ts.map +0 -1
  157. package/dist/lib/runtime-rules/overflow-hidden-on-layout-container.js +0 -220
  158. package/dist/lib/runtime-rules/overflow-hidden-on-layout-container.js.map +0 -1
  159. package/dist/lib/runtime-rules/pass-standard-props.d.ts +0 -19
  160. package/dist/lib/runtime-rules/pass-standard-props.d.ts.map +0 -1
  161. package/dist/lib/runtime-rules/pass-standard-props.js +0 -82
  162. package/dist/lib/runtime-rules/pass-standard-props.js.map +0 -1
  163. package/dist/lib/runtime-rules/prefer-async-await.d.ts +0 -17
  164. package/dist/lib/runtime-rules/prefer-async-await.d.ts.map +0 -1
  165. package/dist/lib/runtime-rules/prefer-async-await.js +0 -52
  166. package/dist/lib/runtime-rules/prefer-async-await.js.map +0 -1
  167. package/dist/lib/runtime-rules/prefer-jsx-syntax.d.ts +0 -17
  168. package/dist/lib/runtime-rules/prefer-jsx-syntax.d.ts.map +0 -1
  169. package/dist/lib/runtime-rules/prefer-jsx-syntax.js +0 -51
  170. package/dist/lib/runtime-rules/prefer-jsx-syntax.js.map +0 -1
  171. package/dist/lib/runtime-rules/prop-state-sync.d.ts +0 -19
  172. package/dist/lib/runtime-rules/prop-state-sync.d.ts.map +0 -1
  173. package/dist/lib/runtime-rules/prop-state-sync.js +0 -76
  174. package/dist/lib/runtime-rules/prop-state-sync.js.map +0 -1
  175. package/dist/lib/runtime-rules/property-name-consistency.d.ts +0 -20
  176. package/dist/lib/runtime-rules/property-name-consistency.d.ts.map +0 -1
  177. package/dist/lib/runtime-rules/property-name-consistency.js +0 -172
  178. package/dist/lib/runtime-rules/property-name-consistency.js.map +0 -1
  179. package/dist/lib/runtime-rules/query-result-field-access-validation.d.ts +0 -10
  180. package/dist/lib/runtime-rules/query-result-field-access-validation.d.ts.map +0 -1
  181. package/dist/lib/runtime-rules/query-result-field-access-validation.js +0 -304
  182. package/dist/lib/runtime-rules/query-result-field-access-validation.js.map +0 -1
  183. package/dist/lib/runtime-rules/react-component-naming.d.ts +0 -19
  184. package/dist/lib/runtime-rules/react-component-naming.d.ts.map +0 -1
  185. package/dist/lib/runtime-rules/react-component-naming.js +0 -72
  186. package/dist/lib/runtime-rules/react-component-naming.js.map +0 -1
  187. package/dist/lib/runtime-rules/react-hooks-rules.d.ts +0 -27
  188. package/dist/lib/runtime-rules/react-hooks-rules.d.ts.map +0 -1
  189. package/dist/lib/runtime-rules/react-hooks-rules.js +0 -223
  190. package/dist/lib/runtime-rules/react-hooks-rules.js.map +0 -1
  191. package/dist/lib/runtime-rules/required-queries-not-called.d.ts +0 -19
  192. package/dist/lib/runtime-rules/required-queries-not-called.d.ts.map +0 -1
  193. package/dist/lib/runtime-rules/required-queries-not-called.js +0 -146
  194. package/dist/lib/runtime-rules/required-queries-not-called.js.map +0 -1
  195. package/dist/lib/runtime-rules/runquery-call-validation.d.ts +0 -11
  196. package/dist/lib/runtime-rules/runquery-call-validation.d.ts.map +0 -1
  197. package/dist/lib/runtime-rules/runquery-call-validation.js +0 -886
  198. package/dist/lib/runtime-rules/runquery-call-validation.js.map +0 -1
  199. package/dist/lib/runtime-rules/runview-call-validation.d.ts +0 -10
  200. package/dist/lib/runtime-rules/runview-call-validation.d.ts.map +0 -1
  201. package/dist/lib/runtime-rules/runview-call-validation.js +0 -336
  202. package/dist/lib/runtime-rules/runview-call-validation.js.map +0 -1
  203. package/dist/lib/runtime-rules/saved-user-settings-pattern.d.ts +0 -19
  204. package/dist/lib/runtime-rules/saved-user-settings-pattern.d.ts.map +0 -1
  205. package/dist/lib/runtime-rules/saved-user-settings-pattern.js +0 -90
  206. package/dist/lib/runtime-rules/saved-user-settings-pattern.js.map +0 -1
  207. package/dist/lib/runtime-rules/search-availability-check.d.ts +0 -9
  208. package/dist/lib/runtime-rules/search-availability-check.d.ts.map +0 -1
  209. package/dist/lib/runtime-rules/search-availability-check.js +0 -220
  210. package/dist/lib/runtime-rules/search-availability-check.js.map +0 -1
  211. package/dist/lib/runtime-rules/search-call-validation.d.ts +0 -9
  212. package/dist/lib/runtime-rules/search-call-validation.d.ts.map +0 -1
  213. package/dist/lib/runtime-rules/search-call-validation.js +0 -336
  214. package/dist/lib/runtime-rules/search-call-validation.js.map +0 -1
  215. package/dist/lib/runtime-rules/server-reload-on-client-operation.d.ts +0 -18
  216. package/dist/lib/runtime-rules/server-reload-on-client-operation.d.ts.map +0 -1
  217. package/dist/lib/runtime-rules/server-reload-on-client-operation.js +0 -107
  218. package/dist/lib/runtime-rules/server-reload-on-client-operation.js.map +0 -1
  219. package/dist/lib/runtime-rules/single-function-only.d.ts +0 -18
  220. package/dist/lib/runtime-rules/single-function-only.d.ts.map +0 -1
  221. package/dist/lib/runtime-rules/single-function-only.js +0 -103
  222. package/dist/lib/runtime-rules/single-function-only.js.map +0 -1
  223. package/dist/lib/runtime-rules/string-replace-all-occurrences.d.ts +0 -19
  224. package/dist/lib/runtime-rules/string-replace-all-occurrences.d.ts.map +0 -1
  225. package/dist/lib/runtime-rules/string-replace-all-occurrences.js +0 -109
  226. package/dist/lib/runtime-rules/string-replace-all-occurrences.js.map +0 -1
  227. package/dist/lib/runtime-rules/string-template-validation.d.ts +0 -22
  228. package/dist/lib/runtime-rules/string-template-validation.d.ts.map +0 -1
  229. package/dist/lib/runtime-rules/string-template-validation.js +0 -163
  230. package/dist/lib/runtime-rules/string-template-validation.js.map +0 -1
  231. package/dist/lib/runtime-rules/styles-validation.d.ts +0 -10
  232. package/dist/lib/runtime-rules/styles-validation.d.ts.map +0 -1
  233. package/dist/lib/runtime-rules/styles-validation.js +0 -153
  234. package/dist/lib/runtime-rules/styles-validation.js.map +0 -1
  235. package/dist/lib/runtime-rules/type-inference-errors.d.ts +0 -23
  236. package/dist/lib/runtime-rules/type-inference-errors.d.ts.map +0 -1
  237. package/dist/lib/runtime-rules/type-inference-errors.js +0 -53
  238. package/dist/lib/runtime-rules/type-inference-errors.js.map +0 -1
  239. package/dist/lib/runtime-rules/type-mismatch-operation.d.ts +0 -23
  240. package/dist/lib/runtime-rules/type-mismatch-operation.d.ts.map +0 -1
  241. package/dist/lib/runtime-rules/type-mismatch-operation.js +0 -141
  242. package/dist/lib/runtime-rules/type-mismatch-operation.js.map +0 -1
  243. package/dist/lib/runtime-rules/undefined-component-usage.d.ts +0 -20
  244. package/dist/lib/runtime-rules/undefined-component-usage.d.ts.map +0 -1
  245. package/dist/lib/runtime-rules/undefined-component-usage.js +0 -138
  246. package/dist/lib/runtime-rules/undefined-component-usage.js.map +0 -1
  247. package/dist/lib/runtime-rules/undefined-jsx-component.d.ts +0 -25
  248. package/dist/lib/runtime-rules/undefined-jsx-component.d.ts.map +0 -1
  249. package/dist/lib/runtime-rules/undefined-jsx-component.js +0 -269
  250. package/dist/lib/runtime-rules/undefined-jsx-component.js.map +0 -1
  251. package/dist/lib/runtime-rules/unsafe-array-operations.d.ts +0 -25
  252. package/dist/lib/runtime-rules/unsafe-array-operations.d.ts.map +0 -1
  253. package/dist/lib/runtime-rules/unsafe-array-operations.js +0 -347
  254. package/dist/lib/runtime-rules/unsafe-array-operations.js.map +0 -1
  255. package/dist/lib/runtime-rules/unsafe-formatting-methods.d.ts +0 -24
  256. package/dist/lib/runtime-rules/unsafe-formatting-methods.d.ts.map +0 -1
  257. package/dist/lib/runtime-rules/unsafe-formatting-methods.js +0 -277
  258. package/dist/lib/runtime-rules/unsafe-formatting-methods.js.map +0 -1
  259. package/dist/lib/runtime-rules/unused-component-dependencies.d.ts +0 -19
  260. package/dist/lib/runtime-rules/unused-component-dependencies.d.ts.map +0 -1
  261. package/dist/lib/runtime-rules/unused-component-dependencies.js +0 -90
  262. package/dist/lib/runtime-rules/unused-component-dependencies.js.map +0 -1
  263. package/dist/lib/runtime-rules/unused-libraries.d.ts +0 -19
  264. package/dist/lib/runtime-rules/unused-libraries.d.ts.map +0 -1
  265. package/dist/lib/runtime-rules/unused-libraries.js +0 -127
  266. package/dist/lib/runtime-rules/unused-libraries.js.map +0 -1
  267. package/dist/lib/runtime-rules/use-function-declaration.d.ts +0 -18
  268. package/dist/lib/runtime-rules/use-function-declaration.d.ts.map +0 -1
  269. package/dist/lib/runtime-rules/use-function-declaration.js +0 -127
  270. package/dist/lib/runtime-rules/use-function-declaration.js.map +0 -1
  271. package/dist/lib/runtime-rules/use-unwrap-components.d.ts +0 -19
  272. package/dist/lib/runtime-rules/use-unwrap-components.d.ts.map +0 -1
  273. package/dist/lib/runtime-rules/use-unwrap-components.js +0 -84
  274. package/dist/lib/runtime-rules/use-unwrap-components.js.map +0 -1
  275. package/dist/lib/runtime-rules/useeffect-unstable-dependencies.d.ts +0 -23
  276. package/dist/lib/runtime-rules/useeffect-unstable-dependencies.d.ts.map +0 -1
  277. package/dist/lib/runtime-rules/useeffect-unstable-dependencies.js +0 -215
  278. package/dist/lib/runtime-rules/useeffect-unstable-dependencies.js.map +0 -1
  279. package/dist/lib/runtime-rules/utilities-api-validation.d.ts +0 -24
  280. package/dist/lib/runtime-rules/utilities-api-validation.d.ts.map +0 -1
  281. package/dist/lib/runtime-rules/utilities-api-validation.js +0 -121
  282. package/dist/lib/runtime-rules/utilities-api-validation.js.map +0 -1
  283. package/dist/lib/runtime-rules/utilities-no-direct-instantiation.d.ts +0 -20
  284. package/dist/lib/runtime-rules/utilities-no-direct-instantiation.d.ts.map +0 -1
  285. package/dist/lib/runtime-rules/utilities-no-direct-instantiation.js +0 -58
  286. package/dist/lib/runtime-rules/utilities-no-direct-instantiation.js.map +0 -1
  287. package/dist/lib/runtime-rules/validate-component-references.d.ts +0 -19
  288. package/dist/lib/runtime-rules/validate-component-references.d.ts.map +0 -1
  289. package/dist/lib/runtime-rules/validate-component-references.js +0 -255
  290. package/dist/lib/runtime-rules/validate-component-references.js.map +0 -1
  291. package/dist/lib/schema-validation/component-prop-rule.d.ts +0 -125
  292. package/dist/lib/schema-validation/component-prop-rule.d.ts.map +0 -1
  293. package/dist/lib/schema-validation/component-prop-rule.js +0 -624
  294. package/dist/lib/schema-validation/component-prop-rule.js.map +0 -1
  295. package/dist/lib/schema-validation/index.d.ts +0 -26
  296. package/dist/lib/schema-validation/index.d.ts.map +0 -1
  297. package/dist/lib/schema-validation/index.js +0 -26
  298. package/dist/lib/schema-validation/index.js.map +0 -1
  299. package/dist/lib/schema-validation/semantic-validators/index.d.ts +0 -23
  300. package/dist/lib/schema-validation/semantic-validators/index.d.ts.map +0 -1
  301. package/dist/lib/schema-validation/semantic-validators/index.js +0 -23
  302. package/dist/lib/schema-validation/semantic-validators/index.js.map +0 -1
  303. package/dist/lib/schema-validation/semantic-validators/required-when-validator.d.ts +0 -43
  304. package/dist/lib/schema-validation/semantic-validators/required-when-validator.d.ts.map +0 -1
  305. package/dist/lib/schema-validation/semantic-validators/required-when-validator.js +0 -94
  306. package/dist/lib/schema-validation/semantic-validators/required-when-validator.js.map +0 -1
  307. package/dist/lib/schema-validation/semantic-validators/semantic-validator-registry.d.ts +0 -122
  308. package/dist/lib/schema-validation/semantic-validators/semantic-validator-registry.d.ts.map +0 -1
  309. package/dist/lib/schema-validation/semantic-validators/semantic-validator-registry.js +0 -166
  310. package/dist/lib/schema-validation/semantic-validators/semantic-validator-registry.js.map +0 -1
  311. package/dist/lib/schema-validation/semantic-validators/semantic-validator.d.ts +0 -260
  312. package/dist/lib/schema-validation/semantic-validators/semantic-validator.d.ts.map +0 -1
  313. package/dist/lib/schema-validation/semantic-validators/semantic-validator.js +0 -301
  314. package/dist/lib/schema-validation/semantic-validators/semantic-validator.js.map +0 -1
  315. package/dist/lib/schema-validation/semantic-validators/sql-where-clause-validator.d.ts +0 -115
  316. package/dist/lib/schema-validation/semantic-validators/sql-where-clause-validator.d.ts.map +0 -1
  317. package/dist/lib/schema-validation/semantic-validators/sql-where-clause-validator.js +0 -381
  318. package/dist/lib/schema-validation/semantic-validators/sql-where-clause-validator.js.map +0 -1
  319. package/dist/lib/schema-validation/semantic-validators/subset-of-entity-fields-validator.d.ts +0 -60
  320. package/dist/lib/schema-validation/semantic-validators/subset-of-entity-fields-validator.d.ts.map +0 -1
  321. package/dist/lib/schema-validation/semantic-validators/subset-of-entity-fields-validator.js +0 -195
  322. package/dist/lib/schema-validation/semantic-validators/subset-of-entity-fields-validator.js.map +0 -1
  323. package/dist/lib/schema-validation/semantic-validators/validation-context.d.ts +0 -335
  324. package/dist/lib/schema-validation/semantic-validators/validation-context.d.ts.map +0 -1
  325. package/dist/lib/schema-validation/semantic-validators/validation-context.js +0 -13
  326. package/dist/lib/schema-validation/semantic-validators/validation-context.js.map +0 -1
  327. package/dist/lib/styles-type-analyzer.d.ts +0 -64
  328. package/dist/lib/styles-type-analyzer.d.ts.map +0 -1
  329. package/dist/lib/styles-type-analyzer.js +0 -242
  330. package/dist/lib/styles-type-analyzer.js.map +0 -1
  331. package/dist/lib/type-context.d.ts +0 -184
  332. package/dist/lib/type-context.d.ts.map +0 -1
  333. package/dist/lib/type-context.js +0 -415
  334. package/dist/lib/type-context.js.map +0 -1
  335. package/dist/lib/type-inference-engine.d.ts +0 -181
  336. package/dist/lib/type-inference-engine.d.ts.map +0 -1
  337. package/dist/lib/type-inference-engine.js +0 -1151
  338. package/dist/lib/type-inference-engine.js.map +0 -1
  339. package/dist/lib/type-rules/type-compatibility-rule.d.ts +0 -79
  340. package/dist/lib/type-rules/type-compatibility-rule.d.ts.map +0 -1
  341. package/dist/lib/type-rules/type-compatibility-rule.js +0 -211
  342. package/dist/lib/type-rules/type-compatibility-rule.js.map +0 -1
@@ -1,1205 +0,0 @@
1
- import * as parser from '@babel/parser';
2
- import _traverse from '@babel/traverse';
3
- import * as t from '@babel/types';
4
- const traverse = ((_traverse.default) ?? _traverse);
5
- import { LibraryLintCache } from './library-lint-cache.js';
6
- import { TypeInferenceEngine } from './type-inference-engine.js';
7
- import { ControlFlowAnalyzer } from './control-flow-analyzer.js';
8
- import { TypeCompatibilityRule } from './type-rules/type-compatibility-rule.js';
9
- import { ComponentPropRule } from './schema-validation/component-prop-rule.js';
10
- import { BaseLintRule } from './lint-rule.js';
11
- import { MJGlobal } from '@memberjunction/global';
12
- import { GetDialect } from '@memberjunction/sql-dialect';
13
- // Side-effect import: triggers @RegisterClass decorators on all built-in rules
14
- import './runtime-rules/index.js';
15
- export class ComponentLinter {
16
- static async validateComponentSyntax(code, componentName) {
17
- try {
18
- const parseResult = parser.parse(code, {
19
- sourceType: 'module',
20
- plugins: ['jsx', 'typescript'],
21
- errorRecovery: true,
22
- ranges: true,
23
- });
24
- if (parseResult.errors && parseResult.errors.length > 0) {
25
- const errors = parseResult.errors.map((error) => {
26
- const location = error.loc ? `Line ${error.loc.line}, Column ${error.loc.column}` : 'Unknown location';
27
- return `${location}: ${error.message || error.toString()}`;
28
- });
29
- return {
30
- valid: false,
31
- errors,
32
- };
33
- }
34
- return {
35
- valid: true,
36
- errors: [],
37
- };
38
- }
39
- catch (error) {
40
- // Handle catastrophic parse failures
41
- const errorMessage = error instanceof Error ? error.message : 'Unknown parsing error';
42
- return {
43
- valid: false,
44
- errors: [`Failed to parse component: ${errorMessage}`],
45
- };
46
- }
47
- }
48
- /**
49
- * The SQL dialect used for WHERE clause validation in semantic validators.
50
- * Defaults to SQL Server. Set via the `sqlDialect` parameter on `lintComponent()`.
51
- */
52
- static { this._sqlDialect = GetDialect('sqlserver'); }
53
- /** Current SQL dialect used for WHERE clause parsing */
54
- static get SqlDialect() {
55
- return ComponentLinter._sqlDialect;
56
- }
57
- static async lintComponent(code, componentName, componentSpec, isRootComponent, contextUser, debugMode, options, sqlDialect) {
58
- if (sqlDialect) {
59
- ComponentLinter._sqlDialect = sqlDialect;
60
- }
61
- try {
62
- // Require contextUser when libraries need to be checked
63
- if (componentSpec?.libraries && componentSpec.libraries.length > 0 && !contextUser) {
64
- throw new Error('contextUser is required when linting components with library dependencies. This is needed to load library-specific lint rules from the database.');
65
- }
66
- // Parse with error recovery to get both AST and errors
67
- const parseResult = parser.parse(code, {
68
- sourceType: 'module',
69
- plugins: ['jsx', 'typescript'],
70
- errorRecovery: true,
71
- attachComment: false,
72
- ranges: true,
73
- tokens: false,
74
- });
75
- // Check for syntax errors from parser
76
- const syntaxViolations = [];
77
- if (parseResult.errors && parseResult.errors.length > 0) {
78
- for (const error of parseResult.errors) {
79
- const err = error; // Babel parser errors don't have proper types
80
- syntaxViolations.push({
81
- rule: 'syntax-error',
82
- severity: 'critical',
83
- line: err.loc?.line || 0,
84
- column: err.loc?.column || 0,
85
- message: `Syntax error in component "${componentName}": ${err.message || err.toString()}`,
86
- code: err.code || 'BABEL_PARSER_ERROR',
87
- });
88
- }
89
- }
90
- // If we have critical syntax errors, return immediately with those
91
- if (syntaxViolations.length > 0) {
92
- // Add suggestions directly to syntax violations
93
- this.generateSyntaxErrorSuggestions(syntaxViolations);
94
- return {
95
- success: false,
96
- violations: syntaxViolations,
97
- criticalCount: syntaxViolations.length,
98
- highCount: 0,
99
- mediumCount: 0,
100
- lowCount: 0,
101
- hasErrors: true,
102
- };
103
- }
104
- // Continue with existing linting logic
105
- const ast = parseResult;
106
- // ═══════════════════════════════════════════════════════════════════════════
107
- // PHASE 1 REFACTOR: Run Type Inference ONCE before all rules
108
- // This creates a shared TypeContext that all rules can consume
109
- // ═══════════════════════════════════════════════════════════════════════════
110
- const typeEngine = new TypeInferenceEngine(componentSpec, contextUser);
111
- const controlFlowAnalyzer = new ControlFlowAnalyzer(ast, componentSpec);
112
- // Run type inference analysis once
113
- await typeEngine.analyze(ast);
114
- const typeContext = typeEngine.getTypeContext();
115
- // Discover all registered lint rules via ClassFactory
116
- const ruleRegistrations = MJGlobal.Instance.ClassFactory.GetAllRegistrations(BaseLintRule);
117
- const allRules = [];
118
- for (const reg of ruleRegistrations) {
119
- if (!reg.Key)
120
- continue; // Skip the base class registration (no key)
121
- const instance = MJGlobal.Instance.ClassFactory.CreateInstance(BaseLintRule, reg.Key);
122
- if (instance)
123
- allRules.push(instance);
124
- }
125
- // Filter rules based on component type
126
- const applicableRules = isRootComponent
127
- ? allRules.filter(rule => rule.AppliesTo === 'all' || rule.AppliesTo === 'root')
128
- : allRules.filter(rule => rule.AppliesTo === 'all' || rule.AppliesTo === 'child');
129
- const violations = [];
130
- // Run each rule with error handling to prevent crashes
131
- for (const rule of applicableRules) {
132
- try {
133
- const ruleViolations = rule.Test(ast, componentName, componentSpec, options, typeContext);
134
- violations.push(...ruleViolations);
135
- }
136
- catch (error) {
137
- console.warn(`Rule "${rule.Name}" failed during execution:`, error instanceof Error ? error.message : error);
138
- if (debugMode) {
139
- console.error('Full error:', error);
140
- }
141
- }
142
- }
143
- // ═══════════════════════════════════════════════════════════════════════════
144
- // PHASE 1 REFACTOR: Run new TypeCompatibilityRule with shared context
145
- // This consolidates all type checking into a single rule
146
- // ═══════════════════════════════════════════════════════════════════════════
147
- try {
148
- const typeCompatRule = new TypeCompatibilityRule();
149
- const lintContext = {
150
- componentName,
151
- componentSpec,
152
- typeContext,
153
- typeEngine,
154
- controlFlowAnalyzer,
155
- sqlDialect: ComponentLinter._sqlDialect,
156
- };
157
- const typeViolations = typeCompatRule.validate(ast, lintContext);
158
- violations.push(...typeViolations);
159
- }
160
- catch (error) {
161
- console.warn('TypeCompatibilityRule failed during execution:', error instanceof Error ? error.message : error);
162
- if (debugMode) {
163
- console.error('Full error:', error);
164
- }
165
- }
166
- // ═══════════════════════════════════════════════════════════════════════════
167
- // PHASE 3 REFACTOR: Run new ComponentPropRule with shared context
168
- // This consolidates all component prop validation into a single rule:
169
- // - Prop existence (from dependency-prop-validation)
170
- // - Required props checking
171
- // - Prop type validation (via TypeContext)
172
- // - Semantic constraint validation (via SemanticValidators)
173
- // - Unknown props warning
174
- // ═══════════════════════════════════════════════════════════════════════════
175
- try {
176
- const componentPropRule = new ComponentPropRule();
177
- const lintContext = {
178
- componentName,
179
- componentSpec,
180
- typeContext,
181
- typeEngine,
182
- controlFlowAnalyzer,
183
- sqlDialect: ComponentLinter._sqlDialect,
184
- };
185
- const propViolations = componentPropRule.validate(ast, lintContext);
186
- violations.push(...propViolations);
187
- }
188
- catch (error) {
189
- console.warn('ComponentPropRule failed during execution:', error instanceof Error ? error.message : error);
190
- if (debugMode) {
191
- console.error('Full error:', error);
192
- }
193
- }
194
- // Add data requirements validation if componentSpec is provided
195
- if (componentSpec?.dataRequirements?.entities) {
196
- try {
197
- const dataViolations = this.validateDataRequirements(ast, componentSpec, options);
198
- violations.push(...dataViolations);
199
- }
200
- catch (error) {
201
- console.warn('Data requirements validation failed:', error instanceof Error ? error.message : error);
202
- if (debugMode) {
203
- console.error('Full error:', error);
204
- }
205
- }
206
- }
207
- // Apply library-specific lint rules if available
208
- if (componentSpec?.libraries) {
209
- const libraryViolations = await this.applyLibraryLintRules(ast, componentSpec, contextUser, debugMode);
210
- violations.push(...libraryViolations);
211
- }
212
- // Deduplicate violations - keep only unique rule+message combinations
213
- const uniqueViolations = this.deduplicateViolations(violations);
214
- // Count violations by severity
215
- const criticalCount = uniqueViolations.filter((v) => v.severity === 'critical').length;
216
- const highCount = uniqueViolations.filter((v) => v.severity === 'high').length;
217
- const mediumCount = uniqueViolations.filter((v) => v.severity === 'medium').length;
218
- const lowCount = uniqueViolations.filter((v) => v.severity === 'low').length;
219
- // Debug mode summary
220
- if (debugMode && uniqueViolations.length > 0) {
221
- console.log('\n' + '='.repeat(60));
222
- console.log('📊 LINT SUMMARY:');
223
- console.log('='.repeat(60));
224
- if (criticalCount > 0)
225
- console.log(` 🔴 Critical: ${criticalCount}`);
226
- if (highCount > 0)
227
- console.log(` 🟠 High: ${highCount}`);
228
- if (mediumCount > 0)
229
- console.log(` 🟡 Medium: ${mediumCount}`);
230
- if (lowCount > 0)
231
- console.log(` 🟢 Low: ${lowCount}`);
232
- console.log('='.repeat(60));
233
- // Group violations by library
234
- const libraryViolations = uniqueViolations.filter((v) => v.rule.includes('-validator'));
235
- if (libraryViolations.length > 0) {
236
- console.log('\n📚 Library-Specific Issues:');
237
- const byLibrary = new Map();
238
- libraryViolations.forEach((v) => {
239
- const lib = v.rule.replace('-validator', '');
240
- if (!byLibrary.has(lib))
241
- byLibrary.set(lib, []);
242
- byLibrary.get(lib).push(v);
243
- });
244
- byLibrary.forEach((violations, library) => {
245
- console.log(` • ${library}: ${violations.length} issue${violations.length > 1 ? 's' : ''}`);
246
- });
247
- }
248
- console.log('');
249
- }
250
- return {
251
- success: criticalCount === 0 && highCount === 0, // Only fail on critical/high
252
- violations: uniqueViolations,
253
- criticalCount,
254
- highCount,
255
- mediumCount,
256
- lowCount,
257
- hasErrors: criticalCount > 0 || highCount > 0,
258
- };
259
- }
260
- catch (error) {
261
- // If parsing fails, return a parse error
262
- // Log stack trace for debugging
263
- if (error instanceof Error && error.stack) {
264
- console.error('Parse error stack trace:', error.stack);
265
- }
266
- return {
267
- success: false,
268
- violations: [
269
- {
270
- rule: 'parse-error',
271
- severity: 'critical',
272
- line: 0,
273
- column: 0,
274
- message: `Failed to parse component: ${error instanceof Error ? error.message : 'Unknown error'}`,
275
- },
276
- ],
277
- hasErrors: true,
278
- };
279
- }
280
- }
281
- static validateDataRequirements(ast, componentSpec, options) {
282
- const violations = [];
283
- // Extract entity names from dataRequirements
284
- const requiredEntities = new Set();
285
- const requiredQueries = new Set();
286
- // Map to store full query definitions for parameter validation
287
- const queryDefinitionsMap = new Map();
288
- // Map to track allowed fields per entity (from dataRequirements display/filter/sort arrays)
289
- const entityFieldsMap = new Map();
290
- // Map to track ALL fields that exist in the entity
291
- // Used to distinguish "field not in requirements" (medium) from "field doesn't exist" (critical)
292
- const entityAllFieldsMap = new Map();
293
- // FIRST: Populate entityAllFieldsMap from options.entityMetadata if provided
294
- // This gives us the complete list of fields that actually exist in each entity
295
- if (options?.entityMetadata && Array.isArray(options.entityMetadata)) {
296
- for (const entity of options.entityMetadata) {
297
- if (entity.name && entity.fields) {
298
- const fieldNames = new Set(entity.fields.map((f) => f.name));
299
- entityAllFieldsMap.set(entity.name, fieldNames);
300
- }
301
- }
302
- }
303
- if (componentSpec.dataRequirements?.entities) {
304
- for (const entity of componentSpec.dataRequirements.entities) {
305
- if (entity.name) {
306
- requiredEntities.add(entity.name);
307
- entityFieldsMap.set(entity.name, {
308
- displayFields: new Set(entity.displayFields || []),
309
- filterFields: new Set(entity.filterFields || []),
310
- sortFields: new Set(entity.sortFields || []),
311
- });
312
- // Build set of ALL fields from fieldMetadata if available
313
- // Only use fieldMetadata as fallback if entityMetadata wasn't provided for this entity
314
- if (!entityAllFieldsMap.has(entity.name) && entity.fieldMetadata && Array.isArray(entity.fieldMetadata)) {
315
- const allFields = new Set();
316
- for (const field of entity.fieldMetadata) {
317
- if (field.name) {
318
- allFields.add(field.name);
319
- }
320
- }
321
- entityAllFieldsMap.set(entity.name, allFields);
322
- }
323
- }
324
- }
325
- }
326
- if (componentSpec.dataRequirements?.queries) {
327
- for (const query of componentSpec.dataRequirements.queries) {
328
- if (query.name) {
329
- requiredQueries.add(query.name);
330
- queryDefinitionsMap.set(query.name, query);
331
- }
332
- }
333
- }
334
- // Also check child components' dataRequirements
335
- if (componentSpec.dependencies) {
336
- for (const dep of componentSpec.dependencies) {
337
- if (dep.dataRequirements?.entities) {
338
- for (const entity of dep.dataRequirements.entities) {
339
- if (entity.name) {
340
- requiredEntities.add(entity.name);
341
- // Merge fields if entity already exists
342
- const existing = entityFieldsMap.get(entity.name);
343
- if (existing) {
344
- (entity.displayFields || []).forEach((f) => existing.displayFields.add(f));
345
- (entity.filterFields || []).forEach((f) => existing.filterFields.add(f));
346
- (entity.sortFields || []).forEach((f) => existing.sortFields.add(f));
347
- }
348
- else {
349
- entityFieldsMap.set(entity.name, {
350
- displayFields: new Set(entity.displayFields || []),
351
- filterFields: new Set(entity.filterFields || []),
352
- sortFields: new Set(entity.sortFields || []),
353
- });
354
- }
355
- // Merge fieldMetadata into allFields map only if entityMetadata wasn't provided
356
- // If entityMetadata was provided, it already has the complete field list
357
- if (!entityAllFieldsMap.has(entity.name) && entity.fieldMetadata && Array.isArray(entity.fieldMetadata)) {
358
- const existingAll = new Set();
359
- for (const field of entity.fieldMetadata) {
360
- if (field.name) {
361
- existingAll.add(field.name);
362
- }
363
- }
364
- entityAllFieldsMap.set(entity.name, existingAll);
365
- }
366
- }
367
- }
368
- }
369
- if (dep.dataRequirements?.queries) {
370
- for (const query of dep.dataRequirements.queries) {
371
- if (query.name) {
372
- requiredQueries.add(query.name);
373
- queryDefinitionsMap.set(query.name, query);
374
- }
375
- }
376
- }
377
- }
378
- }
379
- // Find all RunView, RunViews, and RunQuery calls in the code
380
- traverse(ast, {
381
- CallExpression(path) {
382
- // Check for utilities.rv.RunView or utilities.rv.RunViews pattern
383
- if (t.isMemberExpression(path.node.callee) &&
384
- t.isMemberExpression(path.node.callee.object) &&
385
- t.isIdentifier(path.node.callee.object.object) &&
386
- path.node.callee.object.object.name === 'utilities' &&
387
- t.isIdentifier(path.node.callee.object.property) &&
388
- path.node.callee.object.property.name === 'rv' &&
389
- t.isIdentifier(path.node.callee.property) &&
390
- (path.node.callee.property.name === 'RunView' || path.node.callee.property.name === 'RunViews')) {
391
- // For RunViews, it might be an array of configs
392
- const configs = path.node.callee.property.name === 'RunViews' && path.node.arguments.length > 0 && t.isArrayExpression(path.node.arguments[0])
393
- ? path.node.arguments[0].elements.filter((e) => t.isObjectExpression(e))
394
- : path.node.arguments.length > 0 && t.isObjectExpression(path.node.arguments[0])
395
- ? [path.node.arguments[0]]
396
- : [];
397
- // Check each config object
398
- for (const configObj of configs) {
399
- if (t.isObjectExpression(configObj)) {
400
- // Find EntityName property
401
- for (const prop of configObj.properties) {
402
- if (t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name === 'EntityName' && t.isStringLiteral(prop.value)) {
403
- const usedEntity = prop.value.value;
404
- // Check if this entity is in the required entities
405
- if (requiredEntities.size > 0 && !requiredEntities.has(usedEntity)) {
406
- // Enhanced fuzzy matching for better suggestions
407
- const possibleMatches = Array.from(requiredEntities).filter((e) => {
408
- const eLower = e.toLowerCase();
409
- const usedLower = usedEntity.toLowerCase();
410
- // Check various matching patterns
411
- return (
412
- // Contains match
413
- eLower.includes(usedLower) ||
414
- usedLower.includes(eLower) ||
415
- // Remove spaces and check
416
- eLower.replace(/\s+/g, '').includes(usedLower.replace(/\s+/g, '')) ||
417
- usedLower.replace(/\s+/g, '').includes(eLower.replace(/\s+/g, '')) ||
418
- // Check if the main words match (ignore prefixes like "MJ:")
419
- eLower.replace(/^mj:\s*/i, '').includes(usedLower) ||
420
- usedLower.includes(eLower.replace(/^mj:\s*/i, '')));
421
- });
422
- // Always show all available entities for clarity
423
- const allEntities = Array.from(requiredEntities);
424
- const entityList = allEntities.length <= 5 ? allEntities.join(', ') : allEntities.slice(0, 5).join(', ') + `, ... (${allEntities.length} total)`;
425
- let message = `Entity "${usedEntity}" not found in dataRequirements.`;
426
- if (possibleMatches.length > 0) {
427
- message += ` Did you mean "${possibleMatches[0]}"?`;
428
- }
429
- message += ` Available entities: ${entityList}`;
430
- violations.push({
431
- rule: 'entity-name-mismatch',
432
- severity: 'critical',
433
- line: prop.value.loc?.start.line || 0,
434
- column: prop.value.loc?.start.column || 0,
435
- message,
436
- code: `EntityName: "${usedEntity}"`,
437
- });
438
- }
439
- else {
440
- // Entity is valid, now check fields
441
- const entityFields = entityFieldsMap.get(usedEntity);
442
- if (entityFields) {
443
- // Check Fields array
444
- const fieldsProperty = configObj.properties.find((p) => t.isObjectProperty(p) && t.isIdentifier(p.key) && p.key.name === 'Fields');
445
- if (fieldsProperty && t.isObjectProperty(fieldsProperty) && t.isArrayExpression(fieldsProperty.value)) {
446
- for (const fieldElement of fieldsProperty.value.elements) {
447
- if (t.isStringLiteral(fieldElement)) {
448
- const fieldName = fieldElement.value;
449
- // Check for SQL functions
450
- if (/COUNT\s*\(|SUM\s*\(|AVG\s*\(|MAX\s*\(|MIN\s*\(/i.test(fieldName)) {
451
- violations.push({
452
- rule: 'runview-sql-function',
453
- severity: 'critical',
454
- line: fieldElement.loc?.start.line || 0,
455
- column: fieldElement.loc?.start.column || 0,
456
- message: `RunView does not support SQL aggregations. Use RunQuery for aggregations or fetch raw data and aggregate in JavaScript.`,
457
- code: fieldName,
458
- });
459
- }
460
- else {
461
- // Check if field is in allowed fields
462
- const isAllowed = entityFields.displayFields.has(fieldName) || entityFields.filterFields.has(fieldName) || entityFields.sortFields.has(fieldName);
463
- if (!isAllowed) {
464
- // Check if field exists in entity metadata (two-tier severity)
465
- const allFields = entityAllFieldsMap.get(usedEntity);
466
- const existsInEntity = allFields ? allFields.has(fieldName) : false;
467
- if (existsInEntity) {
468
- // Field exists but not in dataRequirements - medium severity (works but suboptimal)
469
- violations.push({
470
- rule: 'field-not-in-requirements',
471
- severity: 'medium',
472
- line: fieldElement.loc?.start.line || 0,
473
- column: fieldElement.loc?.start.column || 0,
474
- message: `Field "${fieldName}" exists in entity "${usedEntity}" but not declared in dataRequirements. Consider adding to displayFields, filterFields, or sortFields.`,
475
- code: fieldName,
476
- });
477
- }
478
- else {
479
- // Field doesn't exist in entity - critical severity (will fail at runtime)
480
- violations.push({
481
- rule: 'field-not-in-requirements',
482
- severity: 'critical',
483
- line: fieldElement.loc?.start.line || 0,
484
- column: fieldElement.loc?.start.column || 0,
485
- message: `Field "${fieldName}" does not exist in entity "${usedEntity}". Available fields: ${[
486
- ...entityFields.displayFields,
487
- ...entityFields.filterFields,
488
- ...entityFields.sortFields,
489
- ].join(', ')}`,
490
- code: fieldName,
491
- });
492
- }
493
- }
494
- }
495
- }
496
- }
497
- }
498
- // Check OrderBy field
499
- const orderByProperty = configObj.properties.find((p) => t.isObjectProperty(p) && t.isIdentifier(p.key) && p.key.name === 'OrderBy');
500
- if (orderByProperty && t.isObjectProperty(orderByProperty) && t.isStringLiteral(orderByProperty.value)) {
501
- const orderByValue = orderByProperty.value.value;
502
- // Extract field name from OrderBy (e.g., "AccountName ASC" -> "AccountName")
503
- const orderByField = orderByValue.split(/\s+/)[0];
504
- if (!entityFields.sortFields.has(orderByField)) {
505
- // Check if field exists in entity metadata (two-tier severity)
506
- const allFields = entityAllFieldsMap.get(usedEntity);
507
- const existsInEntity = allFields ? allFields.has(orderByField) : false;
508
- if (existsInEntity) {
509
- // Field exists but not in sortFields - medium severity (works but suboptimal)
510
- violations.push({
511
- rule: 'orderby-field-not-sortable',
512
- severity: 'medium',
513
- line: orderByProperty.value.loc?.start.line || 0,
514
- column: orderByProperty.value.loc?.start.column || 0,
515
- message: `OrderBy field "${orderByField}" exists in entity "${usedEntity}" but not declared in sortFields. Consider adding for optimization.`,
516
- code: orderByValue,
517
- });
518
- }
519
- else {
520
- // Field doesn't exist in entity - critical severity (will fail at runtime)
521
- violations.push({
522
- rule: 'orderby-field-not-sortable',
523
- severity: 'critical',
524
- line: orderByProperty.value.loc?.start.line || 0,
525
- column: orderByProperty.value.loc?.start.column || 0,
526
- message: `OrderBy field "${orderByField}" does not exist in entity "${usedEntity}". Available sort fields: ${[
527
- ...entityFields.sortFields,
528
- ].join(', ')}`,
529
- code: orderByValue,
530
- });
531
- }
532
- }
533
- }
534
- }
535
- }
536
- }
537
- }
538
- }
539
- }
540
- }
541
- // Check for utilities.rv.RunQuery pattern
542
- if (t.isMemberExpression(path.node.callee) &&
543
- t.isMemberExpression(path.node.callee.object) &&
544
- t.isIdentifier(path.node.callee.object.object) &&
545
- path.node.callee.object.object.name === 'utilities' &&
546
- t.isIdentifier(path.node.callee.object.property) &&
547
- path.node.callee.object.property.name === 'rv' &&
548
- t.isIdentifier(path.node.callee.property) &&
549
- path.node.callee.property.name === 'RunQuery') {
550
- // Check the first argument (should be an object with QueryName)
551
- if (path.node.arguments.length > 0 && t.isObjectExpression(path.node.arguments[0])) {
552
- const configObj = path.node.arguments[0];
553
- // Find QueryName property
554
- for (const prop of configObj.properties) {
555
- if (t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name === 'QueryName' && t.isStringLiteral(prop.value)) {
556
- const usedQuery = prop.value.value;
557
- // Check if this query is in the required queries
558
- if (requiredQueries.size > 0 && !requiredQueries.has(usedQuery)) {
559
- // Enhanced fuzzy matching for better suggestions
560
- const possibleMatches = Array.from(requiredQueries).filter((q) => {
561
- const qLower = q.toLowerCase();
562
- const usedLower = usedQuery.toLowerCase();
563
- return (
564
- // Contains match
565
- qLower.includes(usedLower) ||
566
- usedLower.includes(qLower) ||
567
- // Remove spaces and check
568
- qLower.replace(/\s+/g, '').includes(usedLower.replace(/\s+/g, '')) ||
569
- usedLower.replace(/\s+/g, '').includes(qLower.replace(/\s+/g, '')));
570
- });
571
- // Always show all available queries for clarity
572
- const allQueries = Array.from(requiredQueries);
573
- const queryList = allQueries.length <= 5 ? allQueries.join(', ') : allQueries.slice(0, 5).join(', ') + `, ... (${allQueries.length} total)`;
574
- let message = `Query "${usedQuery}" not found in dataRequirements.`;
575
- if (possibleMatches.length > 0) {
576
- message += ` Did you mean "${possibleMatches[0]}"?`;
577
- }
578
- if (requiredQueries.size > 0) {
579
- message += ` Available queries: ${queryList}`;
580
- }
581
- else {
582
- message += ` No queries defined in dataRequirements.`;
583
- }
584
- violations.push({
585
- rule: 'query-name-mismatch',
586
- severity: 'critical',
587
- line: prop.value.loc?.start.line || 0,
588
- column: prop.value.loc?.start.column || 0,
589
- message,
590
- code: `QueryName: "${usedQuery}"`,
591
- });
592
- }
593
- else if (queryDefinitionsMap.has(usedQuery)) {
594
- // Query is valid, now check parameters
595
- const queryDef = queryDefinitionsMap.get(usedQuery);
596
- if (queryDef?.parameters && queryDef.parameters.length > 0) {
597
- // Extract parameters from the RunQuery call
598
- const paramsInCall = new Map();
599
- // Look for Parameters property in the config object
600
- for (const prop of configObj.properties) {
601
- if (t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name === 'Parameters' && t.isObjectExpression(prop.value)) {
602
- // Extract each parameter from the Parameters object
603
- for (const paramProp of prop.value.properties) {
604
- if (t.isObjectProperty(paramProp) && t.isIdentifier(paramProp.key)) {
605
- paramsInCall.set(paramProp.key.name, paramProp);
606
- }
607
- }
608
- // Check for required parameters
609
- const requiredParams = queryDef.parameters.filter((p) => p.value !== '@runtime' || p.value === '@runtime');
610
- for (const reqParam of requiredParams) {
611
- if (!paramsInCall.has(reqParam.name)) {
612
- violations.push({
613
- rule: 'missing-query-parameter',
614
- severity: 'critical',
615
- line: prop.value.loc?.start.line || 0,
616
- column: prop.value.loc?.start.column || 0,
617
- message: `Missing required parameter "${reqParam.name}" for query "${usedQuery}". ${reqParam.description ? `Description: ${reqParam.description}` : ''}`,
618
- code: `Parameters: { ${reqParam.name}: ... }`,
619
- });
620
- }
621
- }
622
- // Check for unknown parameters
623
- const validParamNames = new Set(queryDef.parameters.map((p) => p.name));
624
- for (const [paramName, paramNode] of paramsInCall) {
625
- if (!validParamNames.has(paramName)) {
626
- violations.push({
627
- rule: 'unknown-query-parameter',
628
- severity: 'high',
629
- line: paramNode.loc?.start.line || 0,
630
- column: paramNode.loc?.start.column || 0,
631
- message: `Unknown parameter "${paramName}" for query "${usedQuery}". Valid parameters: ${Array.from(validParamNames).join(', ')}`,
632
- code: `${paramName}: ...`,
633
- });
634
- }
635
- }
636
- break; // Found Parameters property, no need to continue
637
- }
638
- }
639
- // If query has parameters but no Parameters property was found in the call
640
- if (paramsInCall.size === 0 && queryDef?.parameters && queryDef.parameters.length > 0) {
641
- violations.push({
642
- rule: 'missing-parameters-object',
643
- severity: 'critical',
644
- line: configObj.loc?.start.line || 0,
645
- column: configObj.loc?.start.column || 0,
646
- message: `Query "${usedQuery}" requires parameters but none were provided. Required parameters: ${queryDef.parameters.map((p) => p.name).join(', ')}`,
647
- code: `RunQuery({ QueryName: "${usedQuery}", Parameters: { ... } })`,
648
- });
649
- }
650
- }
651
- }
652
- }
653
- }
654
- }
655
- }
656
- },
657
- });
658
- return violations;
659
- }
660
- static deduplicateViolations(violations) {
661
- const seen = new Set();
662
- const unique = [];
663
- for (const violation of violations) {
664
- // Create a key from the complete violation details (case-insensitive for message)
665
- const key = `${violation.rule}:${violation.severity}:${violation.line}:${violation.column}:${violation.message.toLowerCase()}`;
666
- if (!seen.has(key)) {
667
- seen.add(key);
668
- unique.push(violation);
669
- }
670
- }
671
- // Sort by severity (critical > high > medium > low) and then by line number
672
- const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
673
- unique.sort((a, b) => {
674
- const severityDiff = severityOrder[a.severity] - severityOrder[b.severity];
675
- if (severityDiff !== 0)
676
- return severityDiff;
677
- return a.line - b.line;
678
- });
679
- return unique;
680
- }
681
- static generateSyntaxErrorSuggestions(violations) {
682
- for (const violation of violations) {
683
- if (violation.message.includes('Unterminated string')) {
684
- violation.suggestion = {
685
- text: 'Check that all string literals are properly closed with matching quotes',
686
- example: 'Template literals with interpolation must use backticks: `text ${variable} text`',
687
- };
688
- }
689
- else if (violation.message.includes('Unexpected token') || violation.message.includes('export')) {
690
- violation.suggestion = {
691
- text: 'Ensure all code is within the component function body',
692
- example: 'Remove any export statements or code outside the function definition',
693
- };
694
- }
695
- else if (violation.message.includes('import') && violation.message.includes('top level')) {
696
- violation.suggestion = {
697
- text: 'Import statements are not allowed in components - use props instead',
698
- example: 'Access libraries through props: const { React, MaterialUI } = props.components',
699
- };
700
- }
701
- else {
702
- violation.suggestion = {
703
- text: 'Fix the syntax error before the component can be compiled',
704
- example: 'Review the code at the specified line and column for syntax issues',
705
- };
706
- }
707
- }
708
- }
709
- /**
710
- * Apply library-specific lint rules based on ComponentLibrary LintRules field
711
- */
712
- static async applyLibraryLintRules(ast, componentSpec, contextUser, debugMode) {
713
- const violations = [];
714
- try {
715
- // Use the cached and compiled library rules
716
- const cache = LibraryLintCache.getInstance();
717
- await cache.loadLibraryRules(contextUser);
718
- // Check each library that this component uses
719
- if (componentSpec.libraries) {
720
- // Run library checks in parallel for performance
721
- const libraryPromises = componentSpec.libraries.map(async (lib) => {
722
- const libraryViolations = [];
723
- // Get the cached and compiled rules for this library
724
- const compiledRules = cache.getLibraryRules(lib.name);
725
- if (debugMode) {
726
- console.log(`\n 📚 Library: ${lib.name}`);
727
- if (compiledRules) {
728
- console.log(` ┌─ Has lint rules: ✅`);
729
- if (compiledRules.validators) {
730
- console.log(` ├─ Validators: ${Object.keys(compiledRules.validators).length}`);
731
- }
732
- if (compiledRules.initialization) {
733
- console.log(` ├─ Initialization rules: ✅`);
734
- }
735
- if (compiledRules.lifecycle) {
736
- console.log(` ├─ Lifecycle rules: ✅`);
737
- }
738
- console.log(` └─ Starting checks...`);
739
- }
740
- else {
741
- console.log(` └─ No lint rules defined`);
742
- }
743
- }
744
- if (compiledRules) {
745
- const library = compiledRules.library;
746
- const libraryName = library.Name || lib.name;
747
- // Apply initialization rules
748
- if (compiledRules.initialization) {
749
- if (debugMode) {
750
- console.log(` ├─ 🔍 Checking ${libraryName} initialization patterns...`);
751
- }
752
- const initViolations = this.checkLibraryInitialization(ast, libraryName, compiledRules.initialization);
753
- // Debug logging for library violations
754
- if (debugMode && initViolations.length > 0) {
755
- console.log(` │ ⚠️ Found ${initViolations.length} initialization issue${initViolations.length > 1 ? 's' : ''}`);
756
- initViolations.forEach((v) => {
757
- const icon = v.severity === 'critical' ? '🔴' : v.severity === 'high' ? '🟠' : v.severity === 'medium' ? '🟡' : '🟢';
758
- console.log(` │ ${icon} Line ${v.line}: ${v.message}`);
759
- });
760
- }
761
- libraryViolations.push(...initViolations);
762
- }
763
- // Apply lifecycle rules
764
- if (compiledRules.lifecycle) {
765
- if (debugMode) {
766
- console.log(` ├─ 🔄 Checking ${libraryName} lifecycle management...`);
767
- }
768
- const lifecycleViolations = this.checkLibraryLifecycle(ast, libraryName, compiledRules.lifecycle);
769
- // Debug logging for library violations
770
- if (debugMode && lifecycleViolations.length > 0) {
771
- console.log(` │ ⚠️ Found ${lifecycleViolations.length} lifecycle issue${lifecycleViolations.length > 1 ? 's' : ''}`);
772
- lifecycleViolations.forEach((v) => {
773
- const icon = v.severity === 'critical' ? '🔴' : v.severity === 'high' ? '🟠' : v.severity === 'medium' ? '🟡' : '🟢';
774
- console.log(` │ ${icon} Line ${v.line}: ${v.message}`);
775
- });
776
- }
777
- libraryViolations.push(...lifecycleViolations);
778
- }
779
- // Apply options validation
780
- if (compiledRules.options) {
781
- if (debugMode) {
782
- console.log(` ├─ ⚙️ Checking ${libraryName} configuration options...`);
783
- }
784
- const optionsViolations = this.checkLibraryOptions(ast, libraryName, compiledRules.options);
785
- // Debug logging for library violations
786
- if (debugMode && optionsViolations.length > 0) {
787
- console.log(` │ ⚠️ Found ${optionsViolations.length} configuration issue${optionsViolations.length > 1 ? 's' : ''}`);
788
- optionsViolations.forEach((v) => {
789
- const icon = v.severity === 'critical' ? '🔴' : v.severity === 'high' ? '🟠' : v.severity === 'medium' ? '🟡' : '🟢';
790
- console.log(` │ ${icon} Line ${v.line}: ${v.message}`);
791
- });
792
- }
793
- libraryViolations.push(...optionsViolations);
794
- }
795
- // Apply compiled validators (already compiled in cache)
796
- if (compiledRules.validators) {
797
- const validatorViolations = this.executeCompiledValidators(ast, libraryName, library.GlobalVariable || '', compiledRules.validators, debugMode);
798
- libraryViolations.push(...validatorViolations);
799
- }
800
- }
801
- return libraryViolations;
802
- });
803
- // Wait for all library checks to complete
804
- const allLibraryViolations = await Promise.all(libraryPromises);
805
- // Flatten the results
806
- allLibraryViolations.forEach((libViolations) => {
807
- violations.push(...libViolations);
808
- });
809
- }
810
- }
811
- catch (error) {
812
- console.warn('Failed to apply library lint rules:', error);
813
- }
814
- return violations;
815
- }
816
- /**
817
- * Check library initialization patterns (constructor, element type, etc.)
818
- */
819
- static checkLibraryInitialization(ast, libraryName, rules) {
820
- const violations = [];
821
- traverse(ast, {
822
- // Check for new ConstructorName() patterns
823
- NewExpression(path) {
824
- if (t.isIdentifier(path.node.callee) && path.node.callee.name === rules.constructorName) {
825
- // Check if it requires 'new' keyword
826
- if (rules.requiresNew === false) {
827
- violations.push({
828
- rule: 'library-initialization',
829
- severity: 'critical',
830
- line: path.node.loc?.start.line || 0,
831
- column: path.node.loc?.start.column || 0,
832
- message: `${libraryName}: ${rules.constructorName} should not use 'new' keyword`,
833
- code: `${rules.constructorName}(...) // without new`,
834
- });
835
- }
836
- // Check element type if first argument is a ref
837
- if (rules.elementType && path.node.arguments[0]) {
838
- const firstArg = path.node.arguments[0];
839
- // Check if it's chartRef.current or similar
840
- if (t.isMemberExpression(firstArg) && t.isIdentifier(firstArg.property) && firstArg.property.name === 'current') {
841
- // Try to find what element the ref is attached to
842
- const refName = t.isIdentifier(firstArg.object) ? firstArg.object.name : null;
843
- if (refName) {
844
- ComponentLinter.checkRefElementType(ast, refName, rules.elementType, libraryName, violations);
845
- }
846
- }
847
- }
848
- }
849
- },
850
- // Check for function calls without new (if requiresNew is true)
851
- CallExpression(path) {
852
- if (t.isIdentifier(path.node.callee) && path.node.callee.name === rules.constructorName && rules.requiresNew === true) {
853
- violations.push({
854
- rule: 'library-initialization',
855
- severity: 'critical',
856
- line: path.node.loc?.start.line || 0,
857
- column: path.node.loc?.start.column || 0,
858
- message: `${libraryName}: ${rules.constructorName} requires 'new' keyword`,
859
- code: `new ${rules.constructorName}(...)`,
860
- });
861
- }
862
- },
863
- });
864
- return violations;
865
- }
866
- /**
867
- * Check if a library is directly instantiated in the component code
868
- * Returns false if the library is only used indirectly (e.g., by dependency components)
869
- */
870
- static isLibraryDirectlyInstantiated(ast, constructorName) {
871
- let isDirectlyUsed = false;
872
- traverse(ast, {
873
- // Check for: new Chart(...), new ApexCharts(...), etc.
874
- NewExpression(path) {
875
- if (t.isIdentifier(path.node.callee) && path.node.callee.name === constructorName) {
876
- isDirectlyUsed = true;
877
- }
878
- },
879
- // Check for: Chart.register(...), Chart.defaults.set(...), etc.
880
- CallExpression(path) {
881
- if (t.isMemberExpression(path.node.callee) &&
882
- t.isIdentifier(path.node.callee.object) &&
883
- path.node.callee.object.name === constructorName) {
884
- isDirectlyUsed = true;
885
- }
886
- },
887
- });
888
- return isDirectlyUsed;
889
- }
890
- /**
891
- * Check if a ref is attached to the correct element type
892
- */
893
- static checkRefElementType(ast, refName, expectedType, libraryName, violations) {
894
- traverse(ast, {
895
- JSXElement(path) {
896
- const openingElement = path.node.openingElement;
897
- // Check if this element has a ref attribute
898
- const refAttr = openingElement.attributes.find((attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === 'ref');
899
- if (refAttr && t.isJSXAttribute(refAttr)) {
900
- // Check if the ref value matches our refName
901
- const refValue = refAttr.value;
902
- if (t.isJSXExpressionContainer(refValue) && t.isIdentifier(refValue.expression) && refValue.expression.name === refName) {
903
- // Check element type
904
- const elementName = t.isJSXIdentifier(openingElement.name) ? openingElement.name.name : '';
905
- if (elementName.toLowerCase() !== expectedType.toLowerCase()) {
906
- violations.push({
907
- rule: 'library-element-type',
908
- severity: 'critical',
909
- line: openingElement.loc?.start.line || 0,
910
- column: openingElement.loc?.start.column || 0,
911
- message: `${libraryName} requires a <${expectedType}> element, not <${elementName}>`,
912
- code: `<${expectedType} ref={${refName}}>`,
913
- });
914
- }
915
- }
916
- }
917
- },
918
- });
919
- }
920
- /**
921
- * Check library lifecycle methods (render, destroy, etc.)
922
- */
923
- static checkLibraryLifecycle(ast, libraryName, rules) {
924
- const violations = [];
925
- // Track which methods are called
926
- const calledMethods = new Set();
927
- const instanceVariables = new Set();
928
- traverse(ast, {
929
- // Track instance variables
930
- VariableDeclarator(path) {
931
- if (t.isNewExpression(path.node.init) && t.isIdentifier(path.node.init.callee)) {
932
- if (t.isIdentifier(path.node.id)) {
933
- instanceVariables.add(path.node.id.name);
934
- }
935
- }
936
- },
937
- // Track method calls
938
- CallExpression(path) {
939
- if (t.isMemberExpression(path.node.callee)) {
940
- const callee = path.node.callee;
941
- if (t.isIdentifier(callee.property)) {
942
- const methodName = callee.property.name;
943
- const objectName = t.isIdentifier(callee.object) ? callee.object.name : null;
944
- if (objectName && instanceVariables.has(objectName)) {
945
- calledMethods.add(methodName);
946
- }
947
- }
948
- }
949
- },
950
- });
951
- // Check required methods
952
- if (rules.requiredMethods) {
953
- for (const method of rules.requiredMethods) {
954
- if (!calledMethods.has(method)) {
955
- violations.push({
956
- rule: 'library-lifecycle',
957
- severity: 'high',
958
- line: 0,
959
- column: 0,
960
- message: `${libraryName}: Missing required method call '${method}()' after initialization`,
961
- code: `instance.${method}()`,
962
- });
963
- }
964
- }
965
- }
966
- // Check cleanup in useEffect
967
- if (rules.cleanupMethods && rules.cleanupMethods.length > 0) {
968
- // First, check if the library is directly instantiated in the component
969
- // If it's only used by dependency components, skip cleanup check
970
- const isLibraryDirectlyUsed = ComponentLinter.isLibraryDirectlyInstantiated(ast, rules.constructorName);
971
- if (!isLibraryDirectlyUsed) {
972
- // Library is only used indirectly (e.g., by dependency components)
973
- // No cleanup check needed
974
- return violations;
975
- }
976
- let hasCleanup = false;
977
- // Track local variables that are instances of the library
978
- const libraryInstances = new Set();
979
- traverse(ast, {
980
- CallExpression(path) {
981
- // Check for both useEffect and React.useEffect
982
- const isUseEffect = (t.isIdentifier(path.node.callee) && path.node.callee.name === 'useEffect') ||
983
- (t.isMemberExpression(path.node.callee) &&
984
- t.isIdentifier(path.node.callee.object) &&
985
- path.node.callee.object.name === 'React' &&
986
- t.isIdentifier(path.node.callee.property) &&
987
- path.node.callee.property.name === 'useEffect');
988
- if (isUseEffect) {
989
- const firstArg = path.node.arguments[0];
990
- if (t.isArrowFunctionExpression(firstArg) || t.isFunctionExpression(firstArg)) {
991
- // First, identify local variables that are library instances
992
- // e.g., const chart = new ApexCharts(...)
993
- libraryInstances.clear();
994
- traverse(firstArg, {
995
- VariableDeclarator(varPath) {
996
- if (t.isIdentifier(varPath.node.id) &&
997
- t.isNewExpression(varPath.node.init) &&
998
- t.isIdentifier(varPath.node.init.callee) &&
999
- varPath.node.init.callee.name === rules.constructorName) {
1000
- libraryInstances.add(varPath.node.id.name);
1001
- }
1002
- },
1003
- }, path.scope, path.state, path);
1004
- // Check if it returns a cleanup function
1005
- traverse(firstArg, {
1006
- ReturnStatement(returnPath) {
1007
- if (t.isArrowFunctionExpression(returnPath.node.argument) || t.isFunctionExpression(returnPath.node.argument)) {
1008
- // Check if cleanup function calls destroy
1009
- traverse(returnPath.node.argument, {
1010
- CallExpression(cleanupPath) {
1011
- if (t.isMemberExpression(cleanupPath.node.callee)) {
1012
- const callee = cleanupPath.node.callee;
1013
- // Check if the method name is a cleanup method
1014
- if (t.isIdentifier(callee.property) && rules.cleanupMethods.includes(callee.property.name)) {
1015
- // Pattern 1: instance.destroy() where instance is a tracked library instance
1016
- // e.g., chart.destroy() where chart is from const chart = new ApexCharts(...)
1017
- if (t.isIdentifier(callee.object) && libraryInstances.has(callee.object.name)) {
1018
- hasCleanup = true;
1019
- }
1020
- // Pattern 2: ref.current.destroy() or ref.current._chart.destroy()
1021
- // Any member expression chain ending in cleanup method
1022
- // e.g., chartRef.current._chart.destroy()
1023
- else if (t.isMemberExpression(callee.object)) {
1024
- hasCleanup = true;
1025
- }
1026
- // Pattern 3: d3.select(...).selectAll('*').remove()
1027
- // Chained method calls ending in cleanup
1028
- else if (t.isCallExpression(callee.object)) {
1029
- hasCleanup = true;
1030
- }
1031
- // Pattern 4: Any identifier calling cleanup method
1032
- // e.g., selection.remove() where selection = d3.select(...)
1033
- else if (t.isIdentifier(callee.object)) {
1034
- hasCleanup = true;
1035
- }
1036
- }
1037
- }
1038
- },
1039
- }, returnPath.scope, returnPath.state, returnPath);
1040
- }
1041
- },
1042
- }, path.scope, path.state, path);
1043
- }
1044
- }
1045
- },
1046
- });
1047
- if (!hasCleanup) {
1048
- violations.push({
1049
- rule: 'library-cleanup',
1050
- severity: 'medium',
1051
- line: 0,
1052
- column: 0,
1053
- message: `${libraryName}: Missing cleanup in useEffect. Call ${rules.cleanupMethods.join(' or ')} in cleanup function`,
1054
- code: `useEffect(() => {\n // ... initialization\n return () => {\n instance.${rules.cleanupMethods[0]}();\n };\n}, []);`,
1055
- });
1056
- }
1057
- }
1058
- return violations;
1059
- }
1060
- /**
1061
- * Check library options and configuration
1062
- */
1063
- static checkLibraryOptions(ast, libraryName, rules) {
1064
- const violations = [];
1065
- traverse(ast, {
1066
- ObjectExpression(path) {
1067
- // Check if this might be a config object for the library
1068
- const properties = path.node.properties.filter((p) => t.isObjectProperty(p));
1069
- const propNames = properties.filter((p) => t.isIdentifier(p.key)).map((p) => p.key.name);
1070
- // Check for required properties
1071
- if (rules.requiredProperties) {
1072
- const hasChartType = propNames.some((name) => rules.requiredProperties.includes(name));
1073
- if (hasChartType) {
1074
- // This looks like a config object, check all required props
1075
- for (const required of rules.requiredProperties) {
1076
- if (!propNames.includes(required)) {
1077
- violations.push({
1078
- rule: 'library-options',
1079
- severity: 'high',
1080
- line: path.node.loc?.start.line || 0,
1081
- column: path.node.loc?.start.column || 0,
1082
- message: `${libraryName}: Missing required option '${required}'`,
1083
- code: `${required}: /* value */`,
1084
- });
1085
- }
1086
- }
1087
- }
1088
- }
1089
- // Check property types
1090
- if (rules.propertyTypes) {
1091
- for (const prop of properties) {
1092
- if (t.isObjectProperty(prop) && t.isIdentifier(prop.key)) {
1093
- const propName = prop.key.name;
1094
- const expectedType = rules.propertyTypes[propName];
1095
- if (expectedType) {
1096
- // Check if the value matches expected type
1097
- if (expectedType.includes('array') && !t.isArrayExpression(prop.value)) {
1098
- violations.push({
1099
- rule: 'library-options',
1100
- severity: 'medium',
1101
- line: prop.loc?.start.line || 0,
1102
- column: prop.loc?.start.column || 0,
1103
- message: `${libraryName}: Option '${propName}' should be an array`,
1104
- code: `${propName}: []`,
1105
- });
1106
- }
1107
- }
1108
- }
1109
- }
1110
- }
1111
- },
1112
- });
1113
- return violations;
1114
- }
1115
- /**
1116
- * Execute pre-compiled validators from cache
1117
- */
1118
- static executeCompiledValidators(ast, libraryName, globalVariable, validators, debugMode) {
1119
- const violations = [];
1120
- // Create context object for validators
1121
- const context = {
1122
- libraryName,
1123
- globalVariable,
1124
- instanceVariables: new Set(),
1125
- violations: [], // Validators push violations here
1126
- };
1127
- // First pass: identify library instance variables
1128
- traverse(ast, {
1129
- VariableDeclarator(path) {
1130
- if (t.isNewExpression(path.node.init) && t.isIdentifier(path.node.init.callee)) {
1131
- // Check if it's a library constructor
1132
- if (path.node.init.callee.name === globalVariable) {
1133
- if (t.isIdentifier(path.node.id)) {
1134
- context.instanceVariables.add(path.node.id.name);
1135
- }
1136
- }
1137
- }
1138
- },
1139
- });
1140
- // Execute each compiled validator
1141
- for (const [validatorName, validator] of Object.entries(validators)) {
1142
- if (validator && validator.validateFn) {
1143
- const beforeCount = context.violations.length;
1144
- // Log that we're running this specific validator
1145
- if (debugMode) {
1146
- console.log(` ├─ 🔬 Running ${libraryName} validator: ${validatorName}`);
1147
- if (validator.description) {
1148
- console.log(` │ ℹ️ ${validator.description}`);
1149
- }
1150
- }
1151
- // Traverse AST and apply validator
1152
- traverse(ast, {
1153
- enter(path) {
1154
- try {
1155
- // Validators don't return violations, they push to context.violations
1156
- validator.validateFn(ast, path, t, context);
1157
- }
1158
- catch (error) {
1159
- // Validator execution error - log but don't crash
1160
- console.warn(`Validator ${validatorName} failed:`, error);
1161
- if (debugMode) {
1162
- console.error('Full error:', error);
1163
- }
1164
- }
1165
- },
1166
- });
1167
- // Debug logging for this specific validator
1168
- const newViolations = context.violations.length - beforeCount;
1169
- if (debugMode && newViolations > 0) {
1170
- console.log(` │ ✓ Found ${newViolations} violation${newViolations > 1 ? 's' : ''}`);
1171
- // Show the violations from this validator
1172
- const validatorViolations = context.violations.slice(beforeCount);
1173
- validatorViolations.forEach((v) => {
1174
- const icon = v.type === 'error' || v.severity === 'critical'
1175
- ? '🔴'
1176
- : v.type === 'warning' || v.severity === 'high'
1177
- ? '🟠'
1178
- : v.severity === 'medium'
1179
- ? '🟡'
1180
- : '🟢';
1181
- console.log(` │ ${icon} Line ${v.line || 'unknown'}: ${v.message}`);
1182
- if (v.suggestion) {
1183
- console.log(` │ 💡 ${v.suggestion}`);
1184
- }
1185
- });
1186
- }
1187
- else if (debugMode) {
1188
- console.log(` │ ✓ No violations found`);
1189
- }
1190
- }
1191
- }
1192
- // Convert context violations to standard format
1193
- const standardViolations = context.violations.map((v) => ({
1194
- rule: `${libraryName.toLowerCase()}-validator`,
1195
- severity: v.severity || (v.type === 'error' ? 'critical' : v.type === 'warning' ? 'high' : 'medium'),
1196
- line: v.line || 0,
1197
- column: v.column || 0,
1198
- message: v.message,
1199
- code: v.code,
1200
- }));
1201
- violations.push(...standardViolations);
1202
- return violations;
1203
- }
1204
- }
1205
- //# sourceMappingURL=component-linter.js.map