@mocoto/mahoraga 0.14.1 → 0.14.2

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 (268) hide show
  1. package/dist/analysts/architects/analyst-structure.js +1 -1
  2. package/dist/analysts/architects/archetypes-custom.js +8 -13
  3. package/dist/analysts/architects/detector-archetypes.js +39 -35
  4. package/dist/analysts/architects/orchestrator-archetypes.js +3 -3
  5. package/dist/analysts/architects/signals-project-advanced.js +2 -2
  6. package/dist/analysts/architects/signals-project.js +1 -1
  7. package/dist/analysts/azure-pipelines/analysts/analyst-azure.js +20 -21
  8. package/dist/analysts/azure-pipelines/detectors/detector-azure-structure.js +0 -3
  9. package/dist/analysts/circleci/analysts/analyst-circleci.js +22 -24
  10. package/dist/analysts/circleci/detectors/detector-circleci-structure.js +4 -7
  11. package/dist/analysts/converters/convert-registry.js +1 -1
  12. package/dist/analysts/converters/index.js +1 -1
  13. package/dist/analysts/corrections/auto-fix-engine.js +1 -1
  14. package/dist/analysts/corrections/corrector-structure.js +8 -7
  15. package/dist/analysts/corrections/map-reversion.js +2 -2
  16. package/dist/analysts/corrections/reorganize-barrels.js +3 -3
  17. package/dist/analysts/corrections/rewrite-testes-aliases.js +23 -22
  18. package/dist/analysts/corrections/type-safety/type-analyzer.js +3 -3
  19. package/dist/analysts/corrections/type-safety/type-validator.js +2 -2
  20. package/dist/analysts/corrections/type-safety/usage-analyzer.js +3 -3
  21. package/dist/analysts/css/analysts/analyst-css.js +27 -28
  22. package/dist/analysts/css-in-js/detectors/detector-css-in-js-best-practices.js +8 -15
  23. package/dist/analysts/detectors/detector-architecture.js +2 -2
  24. package/dist/analysts/detectors/detector-bugs-ml.js +8 -8
  25. package/dist/analysts/detectors/detector-code-fragile.js +11 -10
  26. package/dist/analysts/detectors/detector-dependencies.js +5 -5
  27. package/dist/analysts/detectors/detector-duplications.js +5 -8
  28. package/dist/analysts/detectors/detector-monorepo.js +10 -9
  29. package/dist/analysts/detectors/detector-performance.js +2 -1
  30. package/dist/analysts/detectors/detector-recommendation-architecture.js +1 -1
  31. package/dist/analysts/detectors/detector-security.js +1 -1
  32. package/dist/analysts/detectors/detector-structure.js +3 -3
  33. package/dist/analysts/github-actions/analysts/analyst-github-actions.js +5 -8
  34. package/dist/analysts/github-actions/detectors/detector-workflow-accessibility.js +1 -9
  35. package/dist/analysts/github-actions/detectors/detector-workflow-structure.js +1 -4
  36. package/dist/analysts/github-actions/detectors/detector-workflow-trigger-unsafe.js +6 -5
  37. package/dist/analysts/gitlab-ci/analysts/analyst-gitlab-ci.js +2 -2
  38. package/dist/analysts/gitlab-ci/detectors/detector-gitlab-ci-structure.js +0 -3
  39. package/dist/analysts/go/analysts/analyst-best-practices.js +2 -2
  40. package/dist/analysts/go/analysts/analyst-concurrency.js +1 -1
  41. package/dist/analysts/go/analysts/analyst-security.js +1 -1
  42. package/dist/analysts/go/detectors/detector-conventions.js +1 -1
  43. package/dist/analysts/html/analysts/analyst-html.js +10 -12
  44. package/dist/analysts/html/detectors/detector-html.js +3 -3
  45. package/dist/analysts/js-ts/analysts/analyst-commands-cli.js +22 -22
  46. package/dist/analysts/js-ts/analysts/analyst-functions-long.js +5 -5
  47. package/dist/analysts/js-ts/analysts/analyst-patterns-usage.js +6 -2
  48. package/dist/analysts/js-ts/detectors/detector-anti-patterns-async.js +81 -9
  49. package/dist/analysts/js-ts/detectors/detector-constructions-syntactic.js +35 -6
  50. package/dist/analysts/js-ts/detectors/detector-context-smart.js +6 -6
  51. package/dist/analysts/js-ts/detectors/detector-leaks-memory.js +1 -1
  52. package/dist/analysts/js-ts/detectors/detector-types-unsafe.js +33 -2
  53. package/dist/analysts/php/analysts/analyst-php.js +3 -3
  54. package/dist/analysts/php/detectors/detector-php-best-practices.js +5 -5
  55. package/dist/analysts/php/detectors/detector-php-security.js +5 -5
  56. package/dist/analysts/php/scorers/scorer-php.js +2 -2
  57. package/dist/analysts/plugins/detector-documentation.js +5 -6
  58. package/dist/analysts/plugins/detector-markdown.js +7 -7
  59. package/dist/analysts/python/analysts/analyst-python.js +2 -5
  60. package/dist/analysts/react/analysts/analyst-react-hooks.js +63 -8
  61. package/dist/analysts/react/analysts/analyst-react.js +45 -1
  62. package/dist/analysts/react/corrections/correction-react.js +40 -0
  63. package/dist/analysts/react/detectors/detector-react-best-practices.js +4 -4
  64. package/dist/analysts/react/scorers/scorer-react.js +36 -1
  65. package/dist/analysts/rust/analysts/analyst-best-practices.js +42 -3
  66. package/dist/analysts/rust/analysts/analyst-memory.js +2 -2
  67. package/dist/analysts/rust/analysts/analyst-rust.js +52 -5
  68. package/dist/analysts/rust/analysts/analyst-security.js +29 -1
  69. package/dist/analysts/scorers/scorer.js +5 -5
  70. package/dist/analysts/shell/analysts/analyst-shell.js +81 -5
  71. package/dist/analysts/shell/corrections/correction-shell.js +34 -1
  72. package/dist/analysts/shell/detectors/detector-shell-security.js +88 -0
  73. package/dist/analysts/shell/scorers/scorer-shell.js +58 -1
  74. package/dist/analysts/strategists/operator-structure.js +5 -7
  75. package/dist/analysts/strategists/suggestions-contextuais.js +5 -2
  76. package/dist/analysts/svg/analysts/analyst-svg.js +1 -0
  77. package/dist/analysts/tailwind/analysts/analyst-tailwind.js +79 -55
  78. package/dist/analysts/tailwind/corrections/correction-tailwind.js +116 -9
  79. package/dist/analysts/tailwind/detectors/detector-tailwind-best-practices.js +122 -7
  80. package/dist/analysts/tailwind/scorers/scorer-tailwind.js +62 -10
  81. package/dist/analysts/xml/analysts/analyst-xml.js +2 -2
  82. package/dist/app/index.js +1 -0
  83. package/dist/bin/cli-bootstrap.js +6 -4
  84. package/dist/bin/index.js +6 -4
  85. package/dist/caretakers/scoring.js +1 -0
  86. package/dist/cli/commands/command-analysts.js +1 -1
  87. package/dist/cli/commands/command-azure.js +1 -1
  88. package/dist/cli/commands/command-circleci.js +1 -1
  89. package/dist/cli/commands/command-compliance.js +1 -1
  90. package/dist/cli/commands/command-diagnose.js +10 -13
  91. package/dist/cli/commands/command-fix-types.js +9 -32
  92. package/dist/cli/commands/command-formatters.js +2 -2
  93. package/dist/cli/commands/command-gitlab-ci.js +1 -1
  94. package/dist/cli/commands/command-guardian.js +1 -1
  95. package/dist/cli/commands/command-imports.js +6 -6
  96. package/dist/cli/commands/command-jenkins.js +1 -1
  97. package/dist/cli/commands/command-licenses.js +1 -1
  98. package/dist/cli/commands/command-marketplace.js +3 -3
  99. package/dist/cli/commands/command-names.js +3 -8
  100. package/dist/cli/commands/command-perf.js +7 -9
  101. package/dist/cli/commands/command-plugins.js +4 -4
  102. package/dist/cli/diagnostic/auto-fix.js +2 -2
  103. package/dist/cli/diagnostic/exporters/json-exporter.js +1 -1
  104. package/dist/cli/diagnostic/exporters/sharded-exporter.js +0 -3
  105. package/dist/cli/diagnostic/exporters/svg-optimization-exporter.js +1 -1
  106. package/dist/cli/diagnostic/handlers/archetype-handler.js +3 -6
  107. package/dist/cli/diagnostic/handlers/auto-fix-handler.js +4 -4
  108. package/dist/cli/diagnostic/handlers/guardian-handler.js +1 -1
  109. package/dist/cli/diagnostic/processing-diagnostic.js +31 -35
  110. package/dist/cli/diagnostic/processing-helpers.js +2 -2
  111. package/dist/cli/handlers/fix-types-exporter.js +1 -4
  112. package/dist/cli/helpers/display-frame.js +5 -5
  113. package/dist/core/config/auto/auto-fix-config.js +0 -6
  114. package/dist/core/config/chalk-safe.js +2 -2
  115. package/dist/core/config/config.js +6 -8
  116. package/dist/core/config/conventions.js +1 -1
  117. package/dist/core/config/security.js +2 -2
  118. package/dist/core/execution/analysis-cache.js +5 -8
  119. package/dist/core/execution/executor.js +8 -19
  120. package/dist/core/execution/inquisitor.js +24 -21
  121. package/dist/core/execution/results.js +1 -0
  122. package/dist/core/execution/scanner.js +3 -3
  123. package/dist/core/messages/en/analysts/analyst-github-actions-messages.js +1 -1
  124. package/dist/core/messages/en/analysts/analyst-patterns-usage-messages.js +1 -0
  125. package/dist/core/messages/en/analysts/analyst-shell-messages.js +15 -0
  126. package/dist/core/messages/en/analysts/detector-constructions-syntactic-messages.js +6 -0
  127. package/dist/core/messages/en/analysts/detector-js-ts-messages.js +27 -0
  128. package/dist/core/messages/en/analysts/detector-react-messages.js +8 -0
  129. package/dist/core/messages/en/analysts/detector-shell-messages.js +17 -0
  130. package/dist/core/messages/en/analysts/detector-tailwind-messages.js +3 -3
  131. package/dist/core/messages/en/core/plugin-messages.js +31 -3
  132. package/dist/core/messages/en/log/log.js +4 -4
  133. package/dist/core/messages/en/ui/filter-config.js +1 -5
  134. package/dist/core/messages/i18n-system.js +3 -3
  135. package/dist/core/messages/index.js +3 -3
  136. package/dist/core/messages/ja/analysts/analyst-github-actions-messages.js +1 -1
  137. package/dist/core/messages/ja/analysts/analyst-patterns-usage-messages.js +1 -0
  138. package/dist/core/messages/ja/analysts/analyst-shell-messages.js +15 -0
  139. package/dist/core/messages/ja/analysts/detector-constructions-syntactic-messages.js +6 -0
  140. package/dist/core/messages/ja/analysts/detector-js-ts-messages.js +27 -0
  141. package/dist/core/messages/ja/analysts/detector-react-messages.js +8 -0
  142. package/dist/core/messages/ja/analysts/detector-shell-messages.js +17 -0
  143. package/dist/core/messages/ja/analysts/detector-tailwind-messages.js +3 -3
  144. package/dist/core/messages/ja/core/plugin-messages.js +31 -3
  145. package/dist/core/messages/ja/log/log.js +4 -4
  146. package/dist/core/messages/ja/ui/filter-config.js +1 -1
  147. package/dist/core/messages/pt/analysts/analyst-github-actions-messages.js +1 -1
  148. package/dist/core/messages/pt/analysts/analyst-patterns-usage-messages.js +1 -0
  149. package/dist/core/messages/pt/analysts/analyst-shell-messages.js +15 -0
  150. package/dist/core/messages/pt/analysts/detector-constructions-syntactic-messages.js +6 -0
  151. package/dist/core/messages/pt/analysts/detector-js-ts-messages.js +27 -0
  152. package/dist/core/messages/pt/analysts/detector-react-messages.js +8 -0
  153. package/dist/core/messages/pt/analysts/detector-shell-messages.js +17 -0
  154. package/dist/core/messages/pt/analysts/detector-tailwind-messages.js +3 -3
  155. package/dist/core/messages/pt/core/plugin-messages.js +31 -3
  156. package/dist/core/messages/pt/log/log-engine.js +6 -6
  157. package/dist/core/messages/pt/log/log.js +3 -3
  158. package/dist/core/messages/pt/ui/filter-config.js +1 -1
  159. package/dist/core/messages/shared/icons.js +3 -3
  160. package/dist/core/messages/zh/analysts/analyst-github-actions-messages.js +1 -1
  161. package/dist/core/messages/zh/analysts/analyst-patterns-usage-messages.js +1 -0
  162. package/dist/core/messages/zh/analysts/analyst-shell-messages.js +15 -0
  163. package/dist/core/messages/zh/analysts/detector-constructions-syntactic-messages.js +6 -0
  164. package/dist/core/messages/zh/analysts/detector-js-ts-messages.js +27 -0
  165. package/dist/core/messages/zh/analysts/detector-react-messages.js +8 -0
  166. package/dist/core/messages/zh/analysts/detector-shell-messages.js +17 -0
  167. package/dist/core/messages/zh/analysts/detector-tailwind-messages.js +3 -3
  168. package/dist/core/messages/zh/core/plugin-messages.js +31 -3
  169. package/dist/core/messages/zh/log/log.js +3 -3
  170. package/dist/core/messages/zh/ui/filter-config.js +1 -5
  171. package/dist/core/parsing/filters.js +3 -3
  172. package/dist/core/parsing/langs/json.js +1 -1
  173. package/dist/core/parsing/langs/python.js +1 -1
  174. package/dist/core/parsing/langs/typescript.js +1 -1
  175. package/dist/core/parsing/parser.js +2 -2
  176. package/dist/core/registry/file-registry.js +3 -2
  177. package/dist/core/registry/paths.js +3 -3
  178. package/dist/core/reporting/default-reporter.js +2 -2
  179. package/dist/core/schema/version.js +6 -6
  180. package/dist/core/workers/worker-executor.js +3 -3
  181. package/dist/core/workers/worker-pool.js +0 -0
  182. package/dist/guardian/baseline.js +1 -1
  183. package/dist/guardian/gpg.js +3 -1
  184. package/dist/guardian/integrity.js +2 -7
  185. package/dist/guardian/records.js +6 -1
  186. package/dist/guardian/result.js +1 -1
  187. package/dist/guardian/sentinel.js +6 -1
  188. package/dist/guardian/watcher-hidden.js +5 -1
  189. package/dist/licenses/fs-utils.js +4 -1
  190. package/dist/licenses/generate-notices.js +1 -1
  191. package/dist/licenses/header-options.js +0 -1
  192. package/dist/licenses/index.js +1 -0
  193. package/dist/licenses/normalizer.js +10 -9
  194. package/dist/node.loader.js +224 -4
  195. package/dist/reports/analise-async-patterns.js +1 -1
  196. package/dist/reports/filter-smart.js +2 -5
  197. package/dist/reports/generator-report.js +4 -4
  198. package/dist/reports/report-archetypes.js +10 -10
  199. package/dist/reports/report-caretaker-health.js +2 -1
  200. package/dist/sdk/analyzer.js +1 -1
  201. package/dist/shared/data-processing/fragment-report.js +10 -10
  202. package/dist/shared/data-processing/json.js +1 -1
  203. package/dist/shared/data-processing/occurrences.js +3 -2
  204. package/dist/shared/formatters/code-min-formatter.js +1 -1
  205. package/dist/shared/formatters/engines/prettier.js +1 -1
  206. package/dist/shared/formatters/engines/stylelint.js +6 -9
  207. package/dist/shared/formatters/formatter.js +8 -8
  208. package/dist/shared/formatters/formatters/code.js +3 -3
  209. package/dist/shared/formatters/formatters/css.js +2 -10
  210. package/dist/shared/formatters/formatters/dotfiles.js +1 -1
  211. package/dist/shared/formatters/formatters/html.js +1 -5
  212. package/dist/shared/formatters/formatters/json.js +0 -3
  213. package/dist/shared/formatters/formatters/markdown.js +18 -23
  214. package/dist/shared/formatters/formatters/properties.js +2 -2
  215. package/dist/shared/formatters/formatters/shell.js +1 -4
  216. package/dist/shared/formatters/formatters/sql.js +0 -3
  217. package/dist/shared/formatters/formatters/xml.js +5 -7
  218. package/dist/shared/formatters/formatters/yaml.js +2 -7
  219. package/dist/shared/formatters/index.js +1 -1
  220. package/dist/shared/helpers/framework-detector.js +1 -1
  221. package/dist/shared/helpers/json.js +1 -1
  222. package/dist/shared/helpers/magic-constants-whitelist.js +1 -1
  223. package/dist/shared/helpers/masking.js +17 -17
  224. package/dist/shared/helpers/reader-report.js +0 -12
  225. package/dist/shared/helpers/structural-hash.js +3 -2
  226. package/dist/shared/helpers/structure.js +1 -1
  227. package/dist/shared/marketplace/registry.js +1 -1
  228. package/dist/shared/marketplace/remote.js +4 -4
  229. package/dist/shared/persistence/persistence.js +0 -1
  230. package/dist/shared/plugins/core-plugin.js +3 -1
  231. package/dist/shared/plugins/registry.js +3 -2
  232. package/dist/types/caretakers/imports-barrel.js +1 -0
  233. package/dist/types/caretakers/index.js +4 -0
  234. package/dist/types/caretakers/map-reversion.js +1 -0
  235. package/dist/types/caretakers/pruning.js +1 -0
  236. package/dist/types/caretakers/scoring.js +1 -0
  237. package/dist/types/common/analysts.js +1 -1
  238. package/dist/types/core/config/filters.js +1 -0
  239. package/dist/types/core/config/index.js +2 -0
  240. package/dist/types/core/execution/ambiente.js +1 -0
  241. package/dist/types/core/execution/linguagens.js +1 -0
  242. package/dist/types/core/execution/parse-errors.js +1 -0
  243. package/dist/types/core/execution/results.js +1 -0
  244. package/dist/types/core/execution/structure-json.js +1 -0
  245. package/dist/types/core/index.js +1 -0
  246. package/dist/types/core/messages/index.js +1 -0
  247. package/dist/types/core/messages/log.js +1 -0
  248. package/dist/types/core/parsing/babel-narrow.js +1 -0
  249. package/dist/types/core/parsing/index.js +1 -0
  250. package/dist/types/core/runtime.js +1 -0
  251. package/dist/types/guardian/index.js +2 -0
  252. package/dist/types/guardian/integrity.js +7 -0
  253. package/dist/types/guardian/snapshot.js +1 -0
  254. package/dist/types/index.js +4 -4
  255. package/dist/types/licenses/index.js +2 -0
  256. package/dist/types/processing/display.js +16 -13
  257. package/dist/types/processing/filters.js +3 -7
  258. package/dist/types/reports/compliance.js +1 -0
  259. package/dist/types/reports/streaming.js +1 -0
  260. package/dist/types/scripts/index.js +1 -0
  261. package/dist/types/scripts/migrar-aliases.js +1 -0
  262. package/dist/types/sdk/index.js +1 -0
  263. package/dist/types/shared/index.js +2 -0
  264. package/dist/types/vulnerabilities/index.js +2 -0
  265. package/dist/vulnerabilities/npm-audit.js +0 -1
  266. package/dist/vulnerabilities/scanner.js +2 -1
  267. package/dist/vulnerabilities/vulnerabilities.js +0 -1
  268. package/package.json +11 -12
@@ -25,7 +25,7 @@ function collectBestPractices(src, relPath) {
25
25
  // Excessive function length
26
26
  const funcMatches = src.matchAll(/(?:^|\n)\s*func\s+\w+.*?\{/gs);
27
27
  for (const funcMatch of funcMatches) {
28
- const startIdx = (funcMatch.index ?? 0) + (funcMatch[0].length ?? 0);
28
+ const startIdx = funcMatch.index + funcMatch[0].length;
29
29
  let braceCount = 1;
30
30
  let endIdx = startIdx;
31
31
  for (let i = startIdx; i < src.length && braceCount > 0; i++) {
@@ -40,7 +40,7 @@ function collectBestPractices(src, relPath) {
40
40
  const funcBody = src.slice(startIdx, endIdx);
41
41
  const funcLines = funcBody.split('\n').length;
42
42
  if (funcLines > 60) {
43
- const line = lineOf(funcMatch.index ?? 0);
43
+ const line = lineOf(funcMatch.index);
44
44
  ocorrencias.push(warn(messages.GoMensagens.longFunction, relPath, line));
45
45
  }
46
46
  }
@@ -36,7 +36,7 @@ function collectConcurrencyIssues(src, relPath) {
36
36
  }
37
37
  // Select without default case (potential blocking)
38
38
  for (const match of scan.matchAll(/\bselect\s*\{/g)) {
39
- const blockStart = match.index ?? 0;
39
+ const blockStart = match.index;
40
40
  const block = src.slice(blockStart, blockStart + 500);
41
41
  if (!/\bdefault\s*:/m.test(block)) {
42
42
  const line = lineOf(blockStart);
@@ -32,7 +32,7 @@ function collectSecurityIssues(src, relPath) {
32
32
  }
33
33
  // Printf without format string
34
34
  for (const match of scan.matchAll(/\b(fmt\.(?:Print|Sprint|Fprint))\s*\(\s*([^)]+)\)/g)) {
35
- const args = match[2] ?? '';
35
+ const args = match[2];
36
36
  if (/["'`]/.test(args.trim())) {
37
37
  continue;
38
38
  }
@@ -56,7 +56,7 @@ function detectConventions(src, relPath) {
56
56
  if (impMatch) {
57
57
  const path = impMatch[1];
58
58
  if (/\.$/i.test(path)) {
59
- const absIdx = (match.index ?? 0) + block.indexOf(impLine);
59
+ const absIdx = match.index + block.indexOf(impLine);
60
60
  const line = lineOf(absIdx);
61
61
  ocorrencias.push({
62
62
  relPath,
@@ -10,8 +10,8 @@ function warn(message, relPath, line) {
10
10
  }
11
11
  function isElement(node) { return node.type === ElementType.Tag; }
12
12
  function getAttr(el, name) {
13
- const attrs = el.attribs || {};
14
- const valor = attrs[name] ?? attrs[name.toLowerCase()] ?? '';
13
+ const attrs = el.attribs;
14
+ const valor = attrs[name] || attrs[name.toLowerCase()] || '';
15
15
  return typeof valor === 'string' ? valor : '';
16
16
  }
17
17
  function walk(node, visit) {
@@ -32,7 +32,7 @@ function checkDoctype(node) {
32
32
  return /^!doctype\s+html\b/i.test(data);
33
33
  }
34
34
  function validarButton(node, line, relPath, ocorrencias) {
35
- const text = node.children.some(elem => elem.type === ElementType.Text && ((elem).data ?? '').trim().length > 0);
35
+ const text = node.children.some(elem => elem.type === ElementType.Text && (elem).data.trim().length > 0);
36
36
  if (!text && !getAttr(node, 'aria-label') && !getAttr(node, 'title')) {
37
37
  ocorrencias.push(warn(messages.HtmlMensagens.buttonWithoutText, relPath, line));
38
38
  }
@@ -53,7 +53,7 @@ function validarLink(node, line, relPath, ocorrencias) {
53
53
  if (target === '_blank' && !/(noopener|noreferrer)/i.test(getAttr(node, 'rel'))) {
54
54
  ocorrencias.push(warn(messages.HtmlMensagens.linkTargetBlank, relPath, line));
55
55
  }
56
- const attrs = node.attribs || {};
56
+ const attrs = node.attribs;
57
57
  const hasHref = Object.prototype.hasOwnProperty.call(attrs, 'href');
58
58
  const href = getAttr(node, 'href');
59
59
  if (hasHref && href !== '' && href !== '#') {
@@ -71,7 +71,7 @@ function validarImg(node, line, relPath, ocorrencias, isTemplate) {
71
71
  const ariaHidden = getAttr(node, 'aria-hidden').toLowerCase() === 'true';
72
72
  const role = getAttr(node, 'role').toLowerCase();
73
73
  const decorative = ariaHidden || ariaLabel || role === 'presentation' || role === 'none';
74
- const attrs = node.attribs || {};
74
+ const attrs = node.attribs;
75
75
  const dataAttr = Object.keys(attrs).some(chave => /^(data-)?(decorative|icon|symbol)$/i.test(chave));
76
76
  if (!alt && !decorative && !dataAttr && !/\.svg(\?|#|$)/i.test(getAttr(node, 'src'))) {
77
77
  ocorrencias.push(warn(messages.HtmlMensagens.imgWithoutAlt, relPath, line));
@@ -87,7 +87,7 @@ function validarForm(node, line, relPath, ocorrencias) {
87
87
  if (!getAttr(node, 'method')) {
88
88
  ocorrencias.push(warn(messages.HtmlMensagens.formWithoutMethod, relPath, line));
89
89
  }
90
- const attrs = node.attribs || {};
90
+ const attrs = node.attribs;
91
91
  if (!getAttr(node, 'action') && !Object.keys(attrs).some(chave => chave.toLowerCase().startsWith('data-'))) {
92
92
  ocorrencias.push(warn(messages.HtmlMensagens.formWithoutAction, relPath, line));
93
93
  }
@@ -108,7 +108,7 @@ function validarInput(node, line, relPath, ocorrencias) {
108
108
  }
109
109
  function validarScript(node, line, relPath, ocorrencias, isTemplate) {
110
110
  const srcAttr = getAttr(node, 'src');
111
- const children = node.children || [];
111
+ const children = node.children;
112
112
  const text = children.filter((elem) => elem.type === ElementType.Text).map(elem => ((elem).data || '')).join('');
113
113
  if (!srcAttr && text.trim().length > 0) {
114
114
  ocorrencias.push(warn(messages.HtmlMensagens.inlineScript, relPath, line));
@@ -121,7 +121,7 @@ function validarScript(node, line, relPath, ocorrencias, isTemplate) {
121
121
  }
122
122
  }
123
123
  function validarStyle(node, line, relPath, ocorrencias) {
124
- const children = node.children || [];
124
+ const children = node.children;
125
125
  const text = children.filter((elem) => elem.type === ElementType.Text).map(elem => ((elem).data || '')).join('');
126
126
  if (text.trim().length > 0) {
127
127
  ocorrencias.push(warn(messages.HtmlMensagens.inlineStyle, relPath, line));
@@ -140,10 +140,8 @@ const validadores = {
140
140
  };
141
141
  function validateNode(node, tag, line, relPath, ocorrencias, isTemplate) {
142
142
  const validador = validadores[tag];
143
- if (validador) {
144
- validador(node, line, relPath, ocorrencias, isTemplate);
145
- }
146
- const attrs = node.attribs || {};
143
+ validador(node, line, relPath, ocorrencias, isTemplate);
144
+ const attrs = node.attribs;
147
145
  if (Object.keys(attrs).some(attr => INLINE_EVENTS.has(attr.toLowerCase()))) {
148
146
  ocorrencias.push(warn(messages.HtmlMensagens.inlineHandler, relPath, line));
149
147
  }
@@ -171,7 +171,7 @@ function detectSeo(src, relPath) {
171
171
  const targetId = m[1].slice(1);
172
172
  if (!ids.has(targetId)) {
173
173
  const _lineLookup = splitLines(src);
174
- const idx = m.index ?? 0;
174
+ const idx = m.index;
175
175
  const lineNum = src.slice(0, idx).split('\n').length;
176
176
  results.push({ type: 'html-broken-anchor', message: M.brokenAnchor, line: lineNum });
177
177
  }
@@ -182,7 +182,7 @@ function detectSeo(src, relPath) {
182
182
  for (const m of headingMatches) {
183
183
  const level = parseInt(m[1], 10);
184
184
  if (level > lastLevel + 1 && lastLevel > 0) {
185
- const lineNum = src.slice(0, m.index ?? 0).split('\n').length;
185
+ const lineNum = src.slice(0, m.index).split('\n').length;
186
186
  results.push({ type: 'html-heading-hierarchy-skip', message: M.headingHierarchySkip, line: lineNum });
187
187
  }
188
188
  lastLevel = level;
@@ -310,7 +310,7 @@ function detectSemanticHtml(src, relPath) {
310
310
  const prev = ids.get(idValue) ?? 0;
311
311
  ids.set(idValue, prev + 1);
312
312
  if (prev > 0) {
313
- const lineNum = src.slice(0, m.index ?? 0).split('\n').length;
313
+ const lineNum = src.slice(0, m.index).split('\n').length;
314
314
  results.push({ type: 'html-duplicate-id-semantic', message: M.duplicateId, line: lineNum });
315
315
  }
316
316
  }
@@ -4,19 +4,19 @@ import { traverse } from '../../../core/config/index.js';
4
4
  import { messages } from '../../../core/messages/index.js';
5
5
  import { isBabelNode } from '../../../types/index.js';
6
6
  export function extractHandlerInfo(nodeParam) {
7
- if (!nodeParam || !isBabelNode(nodeParam)) {
7
+ if (!isBabelNode(nodeParam)) {
8
8
  return null;
9
9
  }
10
10
  const node = nodeParam;
11
11
  const build = (fn, bodyBlock, isAnonymous) => {
12
12
  const rawParams = Array.isArray(fn.params) ? fn.params : [];
13
13
  const params = rawParams.filter((p) => textoTitulo.isIdentifier(p));
14
- return { func: fn, bodyBlock, isAnonymous, params, totalParams: (fn.params ?? []).length };
14
+ return { func: fn, bodyBlock, isAnonymous, params, totalParams: fn.params.length };
15
15
  };
16
- if (textoTitulo.isFunctionDeclaration(node) && node.body && textoTitulo.isBlockStatement(node.body)) {
16
+ if (textoTitulo.isFunctionDeclaration(node) && textoTitulo.isBlockStatement(node.body)) {
17
17
  return build(node, node.body, !node.id);
18
18
  }
19
- if ((textoTitulo.isFunctionExpression(node) || textoTitulo.isArrowFunctionExpression(node)) && node.body && textoTitulo.isBlockStatement(node.body)) {
19
+ if ((textoTitulo.isFunctionExpression(node) || textoTitulo.isArrowFunctionExpression(node)) && textoTitulo.isBlockStatement(node.body)) {
20
20
  return build(node, node.body, textoTitulo.isFunctionExpression(node) ? !node.id : true);
21
21
  }
22
22
  return null;
@@ -58,8 +58,8 @@ function detectarNewCommand(node, state) {
58
58
  state.hasFrameworkSignal = true;
59
59
  let nome = '';
60
60
  const arg0 = newExpr.arguments[0];
61
- if (arg0 && textoTitulo.isStringLiteral(arg0)) {
62
- nome = (arg0).value;
61
+ if (textoTitulo.isStringLiteral(arg0)) {
62
+ nome = arg0.value;
63
63
  }
64
64
  if (state.comandosInvocados === 0) {
65
65
  state.comandosInvocados++;
@@ -89,7 +89,7 @@ function detectarDiscordJs(node, state) {
89
89
  if (state.comandosInvocados === 0) {
90
90
  state.comandosInvocados++;
91
91
  const arg0 = call.arguments[0];
92
- const nome = prop.name === 'setName' && arg0 && textoTitulo.isStringLiteral(arg0) ? (arg0).value : 'slash-command';
92
+ const nome = prop.name === 'setName' && textoTitulo.isStringLiteral(arg0) ? arg0.value : 'slash-command';
93
93
  state.comandoNomes.push(nome);
94
94
  }
95
95
  return;
@@ -141,10 +141,10 @@ function detectarGenericCommand(node, state, registrar) {
141
141
  }
142
142
  let comandoNome = '';
143
143
  const primeiroArg = call.arguments[0];
144
- if (primeiroArg && textoTitulo.isStringLiteral(primeiroArg)) {
145
- comandoNome = (primeiroArg).value;
144
+ if (textoTitulo.isStringLiteral(primeiroArg)) {
145
+ comandoNome = primeiroArg.value;
146
146
  }
147
- const handler = call.arguments && call.arguments.length > 1 ? call.arguments[1] : undefined;
147
+ const handler = call.arguments.length > 1 ? call.arguments[1] : undefined;
148
148
  registrar(call, comandoNome, handler, 'generico');
149
149
  }
150
150
  function detectarCommanderAction(node, state, registrar) {
@@ -160,7 +160,7 @@ function detectarCommanderAction(node, state, registrar) {
160
160
  if (!textoTitulo.isIdentifier(prop) || prop.name !== 'action') {
161
161
  return;
162
162
  }
163
- const handler = call.arguments && call.arguments[0];
163
+ const handler = call.arguments[0];
164
164
  const nome = extrairNomeDeCadeia(call);
165
165
  registrar(call, nome, handler, 'commander');
166
166
  }
@@ -177,11 +177,11 @@ function detectarCommanderCommand(node, state, registrar) {
177
177
  if (!textoTitulo.isIdentifier(prop) || prop.name !== 'command') {
178
178
  return;
179
179
  }
180
- const args = call.arguments || [];
180
+ const args = call.arguments;
181
181
  const nameArg = args[0];
182
182
  let nome = '';
183
- if (nameArg && textoTitulo.isStringLiteral(nameArg)) {
184
- nome = (nameArg).value;
183
+ if (textoTitulo.isStringLiteral(nameArg)) {
184
+ nome = nameArg.value;
185
185
  }
186
186
  let handler;
187
187
  for (let i = args.length - 1; i >= 0; i--) {
@@ -210,7 +210,7 @@ function detectarYargs(node, state, registrar) {
210
210
  if (!textoTitulo.isIdentifier(obj) || !/yargs?/i.test(obj.name)) {
211
211
  return;
212
212
  }
213
- const args = call.arguments || [];
213
+ const args = call.arguments;
214
214
  let nome = '';
215
215
  if (args[0] && textoTitulo.isStringLiteral(args[0])) {
216
216
  nome = (args[0]).value;
@@ -236,7 +236,7 @@ function detectarOclif(node, state, registrar) {
236
236
  const k = method.key;
237
237
  return textoTitulo.isIdentifier(k) && k.name === 'run';
238
238
  };
239
- const bodyElem = Array.isArray(bodyArray) ? bodyArray.find(isRunMethod) : undefined;
239
+ const bodyElem = Array.isArray(bodyArray) ? bodyArray?.find(isRunMethod) : undefined;
240
240
  if (bodyElem?.body && textoTitulo.isBlockStatement(bodyElem.body)) {
241
241
  registrar(textoTitulo.callExpression(textoTitulo.identifier('oclifRun'), []), classNome, bodyElem, 'oclif');
242
242
  }
@@ -262,14 +262,14 @@ function detectarVarUsage(node, state) {
262
262
  return;
263
263
  }
264
264
  const fnBody = (node).body;
265
- if (!fnBody || !textoTitulo.isBlockStatement(fnBody)) {
265
+ if (!textoTitulo.isBlockStatement(fnBody)) {
266
266
  return;
267
267
  }
268
268
  const createdVars = new Set();
269
- for (const stmt of fnBody.body || []) {
269
+ for (const stmt of fnBody.body) {
270
270
  if (textoTitulo.isVariableDeclaration(stmt)) {
271
271
  for (const decl of stmt.declarations) {
272
- if (decl.id && textoTitulo.isIdentifier(decl.id) && decl.init && textoTitulo.isNewExpression(decl.init) && textoTitulo.isIdentifier(decl.init.callee) && state.commandClassNomes.has(decl.init.callee.name)) {
272
+ if (textoTitulo.isIdentifier(decl.id) && decl.init && textoTitulo.isNewExpression(decl.init) && textoTitulo.isIdentifier(decl.init.callee) && state.commandClassNomes.has(decl.init.callee.name)) {
273
273
  createdVars.add(decl.id.name);
274
274
  }
275
275
  }
@@ -297,7 +297,7 @@ function criarRegistrar(state) {
297
297
  if (comandoNome) {
298
298
  state.comandoNomes.push(comandoNome);
299
299
  }
300
- const userInfo = extractHandlerInfo(handler);
300
+ const userInfo = handler ? extractHandlerInfo(handler) : null;
301
301
  const registro = {
302
302
  comandoNome,
303
303
  id: undefined,
@@ -315,7 +315,7 @@ function criarRegistrar(state) {
315
315
  function extrairNomeDeCadeia(call) {
316
316
  let current = call;
317
317
  const visitados = new Set();
318
- while (current && !visitados.has(current)) {
318
+ while (!visitados.has(current)) {
319
319
  visitados.add(current);
320
320
  if (textoTitulo.isCallExpression(current) && textoTitulo.isMemberExpression(current.callee)) {
321
321
  const callee = current.callee;
@@ -323,7 +323,7 @@ function extrairNomeDeCadeia(call) {
323
323
  if (textoTitulo.isIdentifier(prop) && prop.name === 'command') {
324
324
  const arg0 = current.arguments[0];
325
325
  if (arg0 && textoTitulo.isStringLiteral(arg0)) {
326
- return arg0.value;
326
+ return (arg0).value;
327
327
  }
328
328
  }
329
329
  const owner = callee.object;
@@ -5,9 +5,9 @@ import { criarAnalista, criarOcorrencia, isBabelNode } from '../../../types/inde
5
5
  function isNodePath(x) {
6
6
  return typeof x === 'object' && x !== null && 'node' in x && isBabelNode(x.node) && typeof x.traverse === 'function';
7
7
  }
8
- const LIMITE_LINHAS = config.ANALISE_LIMITES.FUNCOES_LONGAS.MAX_LINHAS ?? 30;
9
- const LIMITE_PARAMETROS = config.ANALISE_LIMITES.FUNCOES_LONGAS.MAX_PARAMETROS ?? 4;
10
- const LIMITE_ANINHAMENTO = config.ANALISE_LIMITES.FUNCOES_LONGAS.MAX_ANINHAMENTO ?? 3;
8
+ const LIMITE_LINHAS = config.ANALISE_LIMITES.FUNCOES_LONGAS.MAX_LINHAS;
9
+ const LIMITE_PARAMETROS = config.ANALISE_LIMITES.FUNCOES_LONGAS.MAX_PARAMETROS;
10
+ const LIMITE_ANINHAMENTO = config.ANALISE_LIMITES.FUNCOES_LONGAS.MAX_ANINHAMENTO;
11
11
  const LIMITE_COMPLEXIDADE_COGNITIVA = 15;
12
12
  const LIMITE_RESPONSABILIDADES = 3;
13
13
  const LIMITE_CICLOMATICA = 10;
@@ -73,7 +73,7 @@ function calcularCiclomatica(body) {
73
73
  WhileStatement() { score++; },
74
74
  DoWhileStatement() { score++; },
75
75
  SwitchCase(path) {
76
- if (!path.node.consequent || path.node.consequent.length === 0) {
76
+ if (path.node.consequent.length === 0) {
77
77
  return;
78
78
  }
79
79
  score++;
@@ -122,7 +122,7 @@ function verificarEarlyReturn(fn) {
122
122
  const fnParent = path.getFunctionParent();
123
123
  if (fnParent?.node === fn) {
124
124
  const grandParent = path.parentPath;
125
- if (grandParent && textoTitulo.isIfStatement(grandParent.node)) {
125
+ if (textoTitulo.isIfStatement(grandParent.node)) {
126
126
  encontrouEarlyReturn = true;
127
127
  }
128
128
  }
@@ -51,7 +51,7 @@ export const analistaPadroesUso = {
51
51
  }
52
52
  let astWrap = astInput;
53
53
  if (!astWrap && contexto?.arquivos) {
54
- const found = contexto.arquivos.find((arquivo) => arquivo.relPath === relPath) ?? contexto.arquivos[0];
54
+ const found = contexto.arquivos?.find((arquivo) => arquivo.relPath === relPath) ?? contexto.arquivos[0];
55
55
  astWrap = found.ast ?? undefined;
56
56
  }
57
57
  const hasNodeProp = (valor) => typeof valor === 'object' && valor !== null && 'node' in valor;
@@ -131,7 +131,7 @@ export const analistaPadroesUso = {
131
131
  // Type assertion as Type without validation
132
132
  if (textoTitulo.isTSAsExpression(node)) {
133
133
  const parent = path.parentPath.node;
134
- if (parent && !textoTitulo.isVariableDeclarator(parent)) {
134
+ if (!textoTitulo.isVariableDeclarator(parent)) {
135
135
  push({ tipo: 'info', mensagem: messages.PadroesUsoMensagens.typeAssertionWithoutValidation, relPath, linha: node.loc?.start.line, coluna: node.loc?.start.column, nivel: 'info' });
136
136
  }
137
137
  }
@@ -163,6 +163,10 @@ export const analistaPadroesUso = {
163
163
  push({ tipo: 'info', mensagem: messages.PadroesUsoMensagens.uselessConditional, relPath, linha: node.loc?.start.line, coluna: node.loc?.start.column, nivel: 'info' });
164
164
  }
165
165
  }
166
+ // satisfies keyword usage (TypeScript 4.9+)
167
+ if (textoTitulo.isTSSatisfiesExpression(node)) {
168
+ push({ tipo: 'info', mensagem: messages.PadroesUsoMensagens.satisfiesUsage, relPath, linha: node.loc?.start.line, coluna: node.loc?.start.column, nivel: 'info' });
169
+ }
166
170
  }
167
171
  });
168
172
  // Magic strings detection (via text scan)
@@ -20,7 +20,7 @@ const visitorAsync = {
20
20
  if (binding && binding.path.isFunction() && !binding.path.node.async) {
21
21
  return;
22
22
  }
23
- const occurrences = state.ocorrencias || [];
23
+ const occurrences = state.ocorrencias;
24
24
  occurrences.push({
25
25
  tipo: 'floating-promise',
26
26
  linha: path.node.loc?.start.line ?? 0,
@@ -40,10 +40,10 @@ const visitorAsync = {
40
40
  const propName = prop.type === 'Identifier' ? prop.name : '';
41
41
  if (propName === 'map' || propName === 'forEach') {
42
42
  const arg = path.node.arguments[0];
43
- if (arg && (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression')) {
43
+ if (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression') {
44
44
  if (arg.async) {
45
45
  const isForEach = propName === 'forEach';
46
- const occurrences = state.ocorrencias || [];
46
+ const occurrences = state.ocorrencias;
47
47
  occurrences.push({
48
48
  tipo: isForEach ? 'async-in-foreach' : 'async-in-map-without-all',
49
49
  linha: path.node.loc?.start.line ?? 0,
@@ -60,7 +60,7 @@ const visitorAsync = {
60
60
  if (propName === 'then' && textoTitulo.isCallExpression(callee.object) && textoTitulo.isMemberExpression(callee.object.callee)) {
61
61
  const innerProp = callee.object.callee.property;
62
62
  if (textoTitulo.isIdentifier(innerProp) && innerProp.name === 'all') {
63
- const _hasCatch = path.parentPath && textoTitulo.isCallExpression(path.parentPath.node) && path.parentPath.node.arguments.length > 0;
63
+ const _hasCatch = textoTitulo.isCallExpression(path.parentPath.node) && path.parentPath.node.arguments.length > 0;
64
64
  let hasErrorHandler = false;
65
65
  try {
66
66
  path.parentPath.traverse({
@@ -72,7 +72,8 @@ const visitorAsync = {
72
72
  });
73
73
  }
74
74
  catch { }
75
- if (!hasErrorHandler) {
75
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare, @typescript-eslint/no-unnecessary-condition
76
+ if (hasErrorHandler === false) {
76
77
  state.ocorrencias.push({
77
78
  tipo: 'promise-all-no-error',
78
79
  linha: path.node.loc?.start.line ?? 0,
@@ -88,7 +89,7 @@ const visitorAsync = {
88
89
  // Promise nesting: .then() inside .then()
89
90
  if (propName === 'then') {
90
91
  const arg = path.node.arguments[0];
91
- if (arg && (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression')) {
92
+ if (arg.type === 'ArrowFunctionExpression' || arg.type === 'FunctionExpression') {
92
93
  const body = arg.body;
93
94
  if (body.type === 'BlockStatement') {
94
95
  for (const stmt of body.body) {
@@ -119,7 +120,7 @@ const visitorAsync = {
119
120
  // Promise constructor anti-pattern: new Promise((resolve, reject) => { ... })
120
121
  if (callee.type === 'Identifier' && callee.name === 'Promise' && path.node.arguments.length === 1) {
121
122
  const executor = path.node.arguments[0];
122
- if (executor && (executor.type === 'ArrowFunctionExpression' || executor.type === 'FunctionExpression')) {
123
+ if (executor.type === 'ArrowFunctionExpression' || executor.type === 'FunctionExpression') {
123
124
  const executorBody = executor.type === 'ArrowFunctionExpression' ? executor.body : executor.body;
124
125
  if (executorBody.type === 'BlockStatement') {
125
126
  for (const stmt of executorBody.body) {
@@ -140,11 +141,12 @@ const visitorAsync = {
140
141
  }
141
142
  }
142
143
  // return await misuse (outside try/catch)
143
- if (callee.type === 'Identifier' && path.parentPath && textoTitulo.isReturnStatement(path.parentPath.node)) {
144
+ if (callee.type === 'Identifier' && textoTitulo.isReturnStatement(path.parentPath.node)) {
144
145
  const fnParent = path.getFunctionParent();
145
146
  if (fnParent) {
146
147
  let inTryCatch = false;
147
148
  let current = path.parentPath;
149
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
148
150
  while (current) {
149
151
  if (textoTitulo.isTryStatement(current.node)) {
150
152
  inTryCatch = true;
@@ -188,6 +190,21 @@ const visitorAsync = {
188
190
  }
189
191
  state.lastAwaitLine = currentLine;
190
192
  },
193
+ // Top-level await and for-await-of detection via text fallback
194
+ // These are checked in the aplicar method
195
+ ForOfStatement(path, state) {
196
+ if (path.node.await) {
197
+ state.ocorrencias.push({
198
+ tipo: 'for-await-of',
199
+ linha: path.node.loc?.start.line ?? 0,
200
+ coluna: path.node.loc?.start.column ?? 0,
201
+ severidade: 'baixa',
202
+ nivel: 'info',
203
+ mensagem: messages.DetectorJsTsMensagens.forAwaitOf,
204
+ relPath: state.relPath
205
+ });
206
+ }
207
+ },
191
208
  Function(path, state) {
192
209
  // Async function without await
193
210
  if (path.node.async) {
@@ -198,7 +215,8 @@ const visitorAsync = {
198
215
  });
199
216
  }
200
217
  catch { }
201
- if (!hasAwait) {
218
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare, @typescript-eslint/no-unnecessary-condition
219
+ if (hasAwait === false) {
202
220
  state.ocorrencias.push({
203
221
  tipo: 'async-without-await',
204
222
  linha: path.node.loc?.start.line ?? 0,
@@ -256,6 +274,60 @@ export const analistaAntiPadroesAsync = {
256
274
  };
257
275
  const nodeToTraverse = ('node' in ast ? ast.node : ast);
258
276
  traverse(nodeToTraverse, visitorAsync, undefined, stateObj);
277
+ // Top-level await detection via text
278
+ if (/^\s*await\s+/m.test(src)) {
279
+ const topLevelAwaitLine = src.match(/^\s*await\s+/m) ? src.substring(0, src.match(/^\s*await\s+/m).index).split('\n').length : 1;
280
+ const fnParentLines = new Set();
281
+ const fnVisitor = {
282
+ Function(path) {
283
+ if (path.node.loc) {
284
+ for (let i = path.node.loc.start.line; i <= path.node.loc.end.line; i++) {
285
+ fnParentLines.add(i);
286
+ }
287
+ }
288
+ }
289
+ };
290
+ traverse(nodeToTraverse, fnVisitor, undefined, {});
291
+ if (!fnParentLines.has(topLevelAwaitLine)) {
292
+ stateObj.ocorrencias.push({
293
+ tipo: 'top-level-await',
294
+ linha: topLevelAwaitLine,
295
+ coluna: 0,
296
+ severidade: 'baixa',
297
+ nivel: 'info',
298
+ mensagem: messages.DetectorJsTsMensagens.topLevelAwait,
299
+ relPath
300
+ });
301
+ }
302
+ }
303
+ // Promise.withResolvers detection via text
304
+ if (/Promise\.withResolvers\s*\(/g.test(src)) {
305
+ const match = /Promise\.withResolvers\s*\(/g.exec(src);
306
+ const linha = match ? src.substring(0, match.index).split('\n').length : 1;
307
+ stateObj.ocorrencias.push({
308
+ tipo: 'promise-with-resolvers',
309
+ linha,
310
+ coluna: 0,
311
+ severidade: 'baixa',
312
+ nivel: 'info',
313
+ mensagem: messages.DetectorJsTsMensagens.promiseWithResolvers,
314
+ relPath
315
+ });
316
+ }
317
+ // Promise.allSettled detection
318
+ if (/Promise\.allSettled\s*\(/g.test(src)) {
319
+ const match = /Promise\.allSettled\s*\(/g.exec(src);
320
+ const linha = match ? src.substring(0, match.index).split('\n').length : 1;
321
+ stateObj.ocorrencias.push({
322
+ tipo: 'promise-all-settled',
323
+ linha,
324
+ coluna: 0,
325
+ severidade: 'baixa',
326
+ nivel: 'info',
327
+ mensagem: messages.DetectorJsTsMensagens.promiseAllSettled,
328
+ relPath
329
+ });
330
+ }
259
331
  return stateObj.ocorrencias.map(ocorrencia => criarOcorrencia({
260
332
  tipo: ocorrencia.tipo ?? 'anti-padroes-async',
261
333
  nivel: ocorrencia.nivel ?? 'aviso',
@@ -39,9 +39,9 @@ export const analistaConstrucoesSintaticas = {
39
39
  }
40
40
  // Destructuring without defaults
41
41
  for (const declarator of path.node.declarations) {
42
- if (declarator.id && textoTitulo.isObjectPattern(declarator.id)) {
42
+ if (textoTitulo.isObjectPattern(declarator.id)) {
43
43
  for (const prop of declarator.id.properties) {
44
- if (textoTitulo.isObjectProperty(prop) && prop.value && textoTitulo.isIdentifier(prop.value) && !prop.value.typeAnnotation) {
44
+ if (textoTitulo.isObjectProperty(prop) && textoTitulo.isIdentifier(prop.value) && !prop.value.typeAnnotation) {
45
45
  if (!prop.value.optional) {
46
46
  pushExtra('destructuring-without-default', 'info', messages.DetectorConstrucoesSintaticasMensagens.destructuringWithoutDefault, path.node.loc?.start.line ?? 1);
47
47
  }
@@ -146,14 +146,18 @@ export const analistaConstrucoesSintaticas = {
146
146
  // Nullish coalescing vs OR misuse: detecting if (x || defaultValue) where x could be '' or 0
147
147
  if (path.node.operator === '||') {
148
148
  const left = path.node.left;
149
- if (left && (textoTitulo.isMemberExpression(left) || textoTitulo.isIdentifier(left))) {
149
+ if (textoTitulo.isMemberExpression(left) || textoTitulo.isIdentifier(left)) {
150
150
  const right = path.node.right;
151
- if (right && (textoTitulo.isStringLiteral(right) || textoTitulo.isNumericLiteral(right))) {
151
+ if (textoTitulo.isStringLiteral(right) || textoTitulo.isNumericLiteral(right)) {
152
152
  pushExtra('nullish-vs-or', 'info', messages.DetectorConstrucoesSintaticasMensagens.nullishVsOrMisuse, path.node.loc?.start.line ?? 1);
153
153
  }
154
154
  }
155
155
  }
156
156
  },
157
+ // ES2025: Promise.withResolvers
158
+ // ES2025: RegExp.escape
159
+ // ES2026: Error.isError
160
+ // ES2026: Array.fromAsync
157
161
  OptionalMemberExpression(path) {
158
162
  // Optional chaining where not needed: obj?.prop where obj is always defined
159
163
  if ('optional' in path.node && path.node.optional) {
@@ -172,7 +176,7 @@ export const analistaConstrucoesSintaticas = {
172
176
  SpreadElement(path) {
173
177
  // Unnecessary object spread: {...obj, prop: val} could be obj.prop = val
174
178
  const parent = path.parentPath;
175
- if (parent && textoTitulo.isObjectExpression(parent.node)) {
179
+ if (textoTitulo.isObjectExpression(parent.node)) {
176
180
  const arg = path.node.argument;
177
181
  if (textoTitulo.isIdentifier(arg) && parent.node.properties.length >= 2) {
178
182
  const otherSpread = parent.node.properties.filter(p => textoTitulo.isSpreadElement(p) && p !== path.node);
@@ -187,7 +191,7 @@ export const analistaConstrucoesSintaticas = {
187
191
  // Getter/setter without validation
188
192
  if (path.node.kind === 'set') {
189
193
  const body = path.node.body;
190
- if (body && textoTitulo.isBlockStatement(body)) {
194
+ if (textoTitulo.isBlockStatement(body)) {
191
195
  let hasValidation = false;
192
196
  for (const stmt of body.body) {
193
197
  if (textoTitulo.isIfStatement(stmt)) {
@@ -202,6 +206,31 @@ export const analistaConstrucoesSintaticas = {
202
206
  }
203
207
  }
204
208
  });
209
+ // Text-based detection for ES2025/ES2026 patterns
210
+ const esPatterns = [
211
+ [/\busing\s+\w+\s*=\s*new\s+/g, 'explicit-resource-management', (linha) => pushExtra('explicit-resource-management', 'info', 'Explicit Resource Management with "using" (ES2026)', linha)],
212
+ [/\bawait\s+using\s+/g, 'explicit-resource-management-async', (linha) => pushExtra('explicit-resource-management-async', 'info', 'Async Resource Management with "await using" (ES2026)', linha)],
213
+ [/\bTemporal\.\w+/g, 'temporal-api-usage', (linha) => pushExtra('temporal-api-usage', 'info', 'Temporal API usage (ES2026)', linha)],
214
+ [/Promise\.withResolvers\s*\(/g, 'promise-with-resolvers', (linha) => pushExtra('promise-with-resolvers', 'info', 'Promise.withResolvers() (ES2025)', linha)],
215
+ [/Promise\.try\s*\(/g, 'promise-try', (linha) => pushExtra('promise-try', 'info', 'Promise.try() (ES2025)', linha)],
216
+ [/RegExp\.escape\s*\(/g, 'regexp-escape', (linha) => pushExtra('regexp-escape', 'info', 'RegExp.escape() (ES2025)', linha)],
217
+ [/Error\.isError\s*\(/g, 'error-is-error', (linha) => pushExtra('error-is-error', 'info', 'Error.isError() (ES2026)', linha)],
218
+ [/Array\.fromAsync\s*\(/g, 'array-from-async', (linha) => pushExtra('array-from-async', 'info', 'Array.fromAsync() (ES2026)', linha)],
219
+ [/\bIterator\.from\s*\(/g, 'iterator-from', (linha) => pushExtra('iterator-from', 'info', 'Iterator.from() (ES2025)', linha)],
220
+ ];
221
+ for (const [pattern, , fn] of esPatterns) {
222
+ let m;
223
+ while ((m = pattern.exec(src)) !== null) {
224
+ const position = m.index;
225
+ if (position == null) {
226
+ continue;
227
+ }
228
+ if (!isInStringOrComment(src, position)) {
229
+ const linha = src.substring(0, position).split('\n').length;
230
+ fn(linha);
231
+ }
232
+ }
233
+ }
205
234
  // Template literal overuse detection via text fallback
206
235
  const templateLiteralPadrao = /`([^`]{0,10})`/g;
207
236
  let match;
@@ -257,8 +257,8 @@ function analisarDependencias(packageJson) {
257
257
  return evidencias;
258
258
  }
259
259
  const deps = {
260
- ...(packageJson.dependencies || {}),
261
- ...(packageJson.devDependencies || {})
260
+ ...(packageJson.dependencies ?? {}),
261
+ ...(packageJson.devDependencies ?? {})
262
262
  };
263
263
  if (config.VERBOSE) {
264
264
  console.log(DetectorContextoExtraMensagens.debugDependencias, Object.keys(deps));
@@ -283,7 +283,7 @@ function analisarScripts(packageJson) {
283
283
  if (!packageJson) {
284
284
  return evidencias;
285
285
  }
286
- const scripts = packageJson.scripts || {};
286
+ const scripts = packageJson.scripts ?? {};
287
287
  for (const [tecnologia, padroes] of Object.entries(PADROES_TECNOLOGIA)) {
288
288
  for (const script of padroes.scripts) {
289
289
  if (Object.keys(scripts).some(key => key.includes(script) || scripts[key].includes(script))) {
@@ -304,7 +304,7 @@ function analisarEstrutura(estruturaDetectada) {
304
304
  for (const [tecnologia, padroes] of Object.entries(PADROES_TECNOLOGIA)) {
305
305
  for (const estrutura of padroes.estrutura) {
306
306
  // Verificação mais rigorosa para evitar falsos positivos
307
- const matchExato = estruturaDetectada.find(dir => {
307
+ const matchExato = estruturaDetectada?.find(dir => {
308
308
  // Match exato do caminho
309
309
  if (dir === estrutura) {
310
310
  return true;
@@ -432,7 +432,7 @@ function analisarPadroesCodigo(arquivos) {
432
432
  */
433
433
  function verificarAntiPatterns(arquivos, tecnologia) {
434
434
  const padraoTecnologia = PADROES_TECNOLOGIA[tecnologia];
435
- if (!padraoTecnologia.antiPatterns) {
435
+ if (padraoTecnologia.antiPatterns.length === 0) {
436
436
  return false; // Sem anti-patterns definidos
437
437
  }
438
438
  for (const arquivo of arquivos) {
@@ -584,7 +584,7 @@ export function detectarContextoInteligente(estruturaDetectada, arquivos, packag
584
584
  let confiancaTotal = calcularConfiancaTotal(evidencias);
585
585
  // Verificação de dependências obrigatórias para evitar falsos positivos
586
586
  const padraoTecnologia = PADROES_TECNOLOGIA[tecnologia];
587
- if (padraoTecnologia && padraoTecnologia.dependencias.length > 0) {
587
+ if (padraoTecnologia.dependencias.length > 0) {
588
588
  const temDependenciaObrigatoria = evidencias.some(erro => erro.tipo === 'dependencia' && padraoTecnologia.dependencias.includes(erro.valor));
589
589
  // Se não tem nenhuma dependência obrigatória, penalizar severamente
590
590
  if (!temDependenciaObrigatoria) {