@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.
- package/dist/analysts/architects/analyst-structure.js +1 -1
- package/dist/analysts/architects/archetypes-custom.js +8 -13
- package/dist/analysts/architects/detector-archetypes.js +39 -35
- package/dist/analysts/architects/orchestrator-archetypes.js +3 -3
- package/dist/analysts/architects/signals-project-advanced.js +2 -2
- package/dist/analysts/architects/signals-project.js +1 -1
- package/dist/analysts/azure-pipelines/analysts/analyst-azure.js +20 -21
- package/dist/analysts/azure-pipelines/detectors/detector-azure-structure.js +0 -3
- package/dist/analysts/circleci/analysts/analyst-circleci.js +22 -24
- package/dist/analysts/circleci/detectors/detector-circleci-structure.js +4 -7
- package/dist/analysts/converters/convert-registry.js +1 -1
- package/dist/analysts/converters/index.js +1 -1
- package/dist/analysts/corrections/auto-fix-engine.js +1 -1
- package/dist/analysts/corrections/corrector-structure.js +8 -7
- package/dist/analysts/corrections/map-reversion.js +2 -2
- package/dist/analysts/corrections/reorganize-barrels.js +3 -3
- package/dist/analysts/corrections/rewrite-testes-aliases.js +23 -22
- package/dist/analysts/corrections/type-safety/type-analyzer.js +3 -3
- package/dist/analysts/corrections/type-safety/type-validator.js +2 -2
- package/dist/analysts/corrections/type-safety/usage-analyzer.js +3 -3
- package/dist/analysts/css/analysts/analyst-css.js +27 -28
- package/dist/analysts/css-in-js/detectors/detector-css-in-js-best-practices.js +8 -15
- package/dist/analysts/detectors/detector-architecture.js +2 -2
- package/dist/analysts/detectors/detector-bugs-ml.js +8 -8
- package/dist/analysts/detectors/detector-code-fragile.js +11 -10
- package/dist/analysts/detectors/detector-dependencies.js +5 -5
- package/dist/analysts/detectors/detector-duplications.js +5 -8
- package/dist/analysts/detectors/detector-monorepo.js +10 -9
- package/dist/analysts/detectors/detector-performance.js +2 -1
- package/dist/analysts/detectors/detector-recommendation-architecture.js +1 -1
- package/dist/analysts/detectors/detector-security.js +1 -1
- package/dist/analysts/detectors/detector-structure.js +3 -3
- package/dist/analysts/github-actions/analysts/analyst-github-actions.js +5 -8
- package/dist/analysts/github-actions/detectors/detector-workflow-accessibility.js +1 -9
- package/dist/analysts/github-actions/detectors/detector-workflow-structure.js +1 -4
- package/dist/analysts/github-actions/detectors/detector-workflow-trigger-unsafe.js +6 -5
- package/dist/analysts/gitlab-ci/analysts/analyst-gitlab-ci.js +2 -2
- package/dist/analysts/gitlab-ci/detectors/detector-gitlab-ci-structure.js +0 -3
- package/dist/analysts/go/analysts/analyst-best-practices.js +2 -2
- package/dist/analysts/go/analysts/analyst-concurrency.js +1 -1
- package/dist/analysts/go/analysts/analyst-security.js +1 -1
- package/dist/analysts/go/detectors/detector-conventions.js +1 -1
- package/dist/analysts/html/analysts/analyst-html.js +10 -12
- package/dist/analysts/html/detectors/detector-html.js +3 -3
- package/dist/analysts/js-ts/analysts/analyst-commands-cli.js +22 -22
- package/dist/analysts/js-ts/analysts/analyst-functions-long.js +5 -5
- package/dist/analysts/js-ts/analysts/analyst-patterns-usage.js +6 -2
- package/dist/analysts/js-ts/detectors/detector-anti-patterns-async.js +81 -9
- package/dist/analysts/js-ts/detectors/detector-constructions-syntactic.js +35 -6
- package/dist/analysts/js-ts/detectors/detector-context-smart.js +6 -6
- package/dist/analysts/js-ts/detectors/detector-leaks-memory.js +1 -1
- package/dist/analysts/js-ts/detectors/detector-types-unsafe.js +33 -2
- package/dist/analysts/php/analysts/analyst-php.js +3 -3
- package/dist/analysts/php/detectors/detector-php-best-practices.js +5 -5
- package/dist/analysts/php/detectors/detector-php-security.js +5 -5
- package/dist/analysts/php/scorers/scorer-php.js +2 -2
- package/dist/analysts/plugins/detector-documentation.js +5 -6
- package/dist/analysts/plugins/detector-markdown.js +7 -7
- package/dist/analysts/python/analysts/analyst-python.js +2 -5
- package/dist/analysts/react/analysts/analyst-react-hooks.js +63 -8
- package/dist/analysts/react/analysts/analyst-react.js +45 -1
- package/dist/analysts/react/corrections/correction-react.js +40 -0
- package/dist/analysts/react/detectors/detector-react-best-practices.js +4 -4
- package/dist/analysts/react/scorers/scorer-react.js +36 -1
- package/dist/analysts/rust/analysts/analyst-best-practices.js +42 -3
- package/dist/analysts/rust/analysts/analyst-memory.js +2 -2
- package/dist/analysts/rust/analysts/analyst-rust.js +52 -5
- package/dist/analysts/rust/analysts/analyst-security.js +29 -1
- package/dist/analysts/scorers/scorer.js +5 -5
- package/dist/analysts/shell/analysts/analyst-shell.js +81 -5
- package/dist/analysts/shell/corrections/correction-shell.js +34 -1
- package/dist/analysts/shell/detectors/detector-shell-security.js +88 -0
- package/dist/analysts/shell/scorers/scorer-shell.js +58 -1
- package/dist/analysts/strategists/operator-structure.js +5 -7
- package/dist/analysts/strategists/suggestions-contextuais.js +5 -2
- package/dist/analysts/svg/analysts/analyst-svg.js +1 -0
- package/dist/analysts/tailwind/analysts/analyst-tailwind.js +79 -55
- package/dist/analysts/tailwind/corrections/correction-tailwind.js +116 -9
- package/dist/analysts/tailwind/detectors/detector-tailwind-best-practices.js +122 -7
- package/dist/analysts/tailwind/scorers/scorer-tailwind.js +62 -10
- package/dist/analysts/xml/analysts/analyst-xml.js +2 -2
- package/dist/app/index.js +1 -0
- package/dist/bin/cli-bootstrap.js +6 -4
- package/dist/bin/index.js +6 -4
- package/dist/caretakers/scoring.js +1 -0
- package/dist/cli/commands/command-analysts.js +1 -1
- package/dist/cli/commands/command-azure.js +1 -1
- package/dist/cli/commands/command-circleci.js +1 -1
- package/dist/cli/commands/command-compliance.js +1 -1
- package/dist/cli/commands/command-diagnose.js +10 -13
- package/dist/cli/commands/command-fix-types.js +9 -32
- package/dist/cli/commands/command-formatters.js +2 -2
- package/dist/cli/commands/command-gitlab-ci.js +1 -1
- package/dist/cli/commands/command-guardian.js +1 -1
- package/dist/cli/commands/command-imports.js +6 -6
- package/dist/cli/commands/command-jenkins.js +1 -1
- package/dist/cli/commands/command-licenses.js +1 -1
- package/dist/cli/commands/command-marketplace.js +3 -3
- package/dist/cli/commands/command-names.js +3 -8
- package/dist/cli/commands/command-perf.js +7 -9
- package/dist/cli/commands/command-plugins.js +4 -4
- package/dist/cli/diagnostic/auto-fix.js +2 -2
- package/dist/cli/diagnostic/exporters/json-exporter.js +1 -1
- package/dist/cli/diagnostic/exporters/sharded-exporter.js +0 -3
- package/dist/cli/diagnostic/exporters/svg-optimization-exporter.js +1 -1
- package/dist/cli/diagnostic/handlers/archetype-handler.js +3 -6
- package/dist/cli/diagnostic/handlers/auto-fix-handler.js +4 -4
- package/dist/cli/diagnostic/handlers/guardian-handler.js +1 -1
- package/dist/cli/diagnostic/processing-diagnostic.js +31 -35
- package/dist/cli/diagnostic/processing-helpers.js +2 -2
- package/dist/cli/handlers/fix-types-exporter.js +1 -4
- package/dist/cli/helpers/display-frame.js +5 -5
- package/dist/core/config/auto/auto-fix-config.js +0 -6
- package/dist/core/config/chalk-safe.js +2 -2
- package/dist/core/config/config.js +6 -8
- package/dist/core/config/conventions.js +1 -1
- package/dist/core/config/security.js +2 -2
- package/dist/core/execution/analysis-cache.js +5 -8
- package/dist/core/execution/executor.js +8 -19
- package/dist/core/execution/inquisitor.js +24 -21
- package/dist/core/execution/results.js +1 -0
- package/dist/core/execution/scanner.js +3 -3
- package/dist/core/messages/en/analysts/analyst-github-actions-messages.js +1 -1
- package/dist/core/messages/en/analysts/analyst-patterns-usage-messages.js +1 -0
- package/dist/core/messages/en/analysts/analyst-shell-messages.js +15 -0
- package/dist/core/messages/en/analysts/detector-constructions-syntactic-messages.js +6 -0
- package/dist/core/messages/en/analysts/detector-js-ts-messages.js +27 -0
- package/dist/core/messages/en/analysts/detector-react-messages.js +8 -0
- package/dist/core/messages/en/analysts/detector-shell-messages.js +17 -0
- package/dist/core/messages/en/analysts/detector-tailwind-messages.js +3 -3
- package/dist/core/messages/en/core/plugin-messages.js +31 -3
- package/dist/core/messages/en/log/log.js +4 -4
- package/dist/core/messages/en/ui/filter-config.js +1 -5
- package/dist/core/messages/i18n-system.js +3 -3
- package/dist/core/messages/index.js +3 -3
- package/dist/core/messages/ja/analysts/analyst-github-actions-messages.js +1 -1
- package/dist/core/messages/ja/analysts/analyst-patterns-usage-messages.js +1 -0
- package/dist/core/messages/ja/analysts/analyst-shell-messages.js +15 -0
- package/dist/core/messages/ja/analysts/detector-constructions-syntactic-messages.js +6 -0
- package/dist/core/messages/ja/analysts/detector-js-ts-messages.js +27 -0
- package/dist/core/messages/ja/analysts/detector-react-messages.js +8 -0
- package/dist/core/messages/ja/analysts/detector-shell-messages.js +17 -0
- package/dist/core/messages/ja/analysts/detector-tailwind-messages.js +3 -3
- package/dist/core/messages/ja/core/plugin-messages.js +31 -3
- package/dist/core/messages/ja/log/log.js +4 -4
- package/dist/core/messages/ja/ui/filter-config.js +1 -1
- package/dist/core/messages/pt/analysts/analyst-github-actions-messages.js +1 -1
- package/dist/core/messages/pt/analysts/analyst-patterns-usage-messages.js +1 -0
- package/dist/core/messages/pt/analysts/analyst-shell-messages.js +15 -0
- package/dist/core/messages/pt/analysts/detector-constructions-syntactic-messages.js +6 -0
- package/dist/core/messages/pt/analysts/detector-js-ts-messages.js +27 -0
- package/dist/core/messages/pt/analysts/detector-react-messages.js +8 -0
- package/dist/core/messages/pt/analysts/detector-shell-messages.js +17 -0
- package/dist/core/messages/pt/analysts/detector-tailwind-messages.js +3 -3
- package/dist/core/messages/pt/core/plugin-messages.js +31 -3
- package/dist/core/messages/pt/log/log-engine.js +6 -6
- package/dist/core/messages/pt/log/log.js +3 -3
- package/dist/core/messages/pt/ui/filter-config.js +1 -1
- package/dist/core/messages/shared/icons.js +3 -3
- package/dist/core/messages/zh/analysts/analyst-github-actions-messages.js +1 -1
- package/dist/core/messages/zh/analysts/analyst-patterns-usage-messages.js +1 -0
- package/dist/core/messages/zh/analysts/analyst-shell-messages.js +15 -0
- package/dist/core/messages/zh/analysts/detector-constructions-syntactic-messages.js +6 -0
- package/dist/core/messages/zh/analysts/detector-js-ts-messages.js +27 -0
- package/dist/core/messages/zh/analysts/detector-react-messages.js +8 -0
- package/dist/core/messages/zh/analysts/detector-shell-messages.js +17 -0
- package/dist/core/messages/zh/analysts/detector-tailwind-messages.js +3 -3
- package/dist/core/messages/zh/core/plugin-messages.js +31 -3
- package/dist/core/messages/zh/log/log.js +3 -3
- package/dist/core/messages/zh/ui/filter-config.js +1 -5
- package/dist/core/parsing/filters.js +3 -3
- package/dist/core/parsing/langs/json.js +1 -1
- package/dist/core/parsing/langs/python.js +1 -1
- package/dist/core/parsing/langs/typescript.js +1 -1
- package/dist/core/parsing/parser.js +2 -2
- package/dist/core/registry/file-registry.js +3 -2
- package/dist/core/registry/paths.js +3 -3
- package/dist/core/reporting/default-reporter.js +2 -2
- package/dist/core/schema/version.js +6 -6
- package/dist/core/workers/worker-executor.js +3 -3
- package/dist/core/workers/worker-pool.js +0 -0
- package/dist/guardian/baseline.js +1 -1
- package/dist/guardian/gpg.js +3 -1
- package/dist/guardian/integrity.js +2 -7
- package/dist/guardian/records.js +6 -1
- package/dist/guardian/result.js +1 -1
- package/dist/guardian/sentinel.js +6 -1
- package/dist/guardian/watcher-hidden.js +5 -1
- package/dist/licenses/fs-utils.js +4 -1
- package/dist/licenses/generate-notices.js +1 -1
- package/dist/licenses/header-options.js +0 -1
- package/dist/licenses/index.js +1 -0
- package/dist/licenses/normalizer.js +10 -9
- package/dist/node.loader.js +224 -4
- package/dist/reports/analise-async-patterns.js +1 -1
- package/dist/reports/filter-smart.js +2 -5
- package/dist/reports/generator-report.js +4 -4
- package/dist/reports/report-archetypes.js +10 -10
- package/dist/reports/report-caretaker-health.js +2 -1
- package/dist/sdk/analyzer.js +1 -1
- package/dist/shared/data-processing/fragment-report.js +10 -10
- package/dist/shared/data-processing/json.js +1 -1
- package/dist/shared/data-processing/occurrences.js +3 -2
- package/dist/shared/formatters/code-min-formatter.js +1 -1
- package/dist/shared/formatters/engines/prettier.js +1 -1
- package/dist/shared/formatters/engines/stylelint.js +6 -9
- package/dist/shared/formatters/formatter.js +8 -8
- package/dist/shared/formatters/formatters/code.js +3 -3
- package/dist/shared/formatters/formatters/css.js +2 -10
- package/dist/shared/formatters/formatters/dotfiles.js +1 -1
- package/dist/shared/formatters/formatters/html.js +1 -5
- package/dist/shared/formatters/formatters/json.js +0 -3
- package/dist/shared/formatters/formatters/markdown.js +18 -23
- package/dist/shared/formatters/formatters/properties.js +2 -2
- package/dist/shared/formatters/formatters/shell.js +1 -4
- package/dist/shared/formatters/formatters/sql.js +0 -3
- package/dist/shared/formatters/formatters/xml.js +5 -7
- package/dist/shared/formatters/formatters/yaml.js +2 -7
- package/dist/shared/formatters/index.js +1 -1
- package/dist/shared/helpers/framework-detector.js +1 -1
- package/dist/shared/helpers/json.js +1 -1
- package/dist/shared/helpers/magic-constants-whitelist.js +1 -1
- package/dist/shared/helpers/masking.js +17 -17
- package/dist/shared/helpers/reader-report.js +0 -12
- package/dist/shared/helpers/structural-hash.js +3 -2
- package/dist/shared/helpers/structure.js +1 -1
- package/dist/shared/marketplace/registry.js +1 -1
- package/dist/shared/marketplace/remote.js +4 -4
- package/dist/shared/persistence/persistence.js +0 -1
- package/dist/shared/plugins/core-plugin.js +3 -1
- package/dist/shared/plugins/registry.js +3 -2
- package/dist/types/caretakers/imports-barrel.js +1 -0
- package/dist/types/caretakers/index.js +4 -0
- package/dist/types/caretakers/map-reversion.js +1 -0
- package/dist/types/caretakers/pruning.js +1 -0
- package/dist/types/caretakers/scoring.js +1 -0
- package/dist/types/common/analysts.js +1 -1
- package/dist/types/core/config/filters.js +1 -0
- package/dist/types/core/config/index.js +2 -0
- package/dist/types/core/execution/ambiente.js +1 -0
- package/dist/types/core/execution/linguagens.js +1 -0
- package/dist/types/core/execution/parse-errors.js +1 -0
- package/dist/types/core/execution/results.js +1 -0
- package/dist/types/core/execution/structure-json.js +1 -0
- package/dist/types/core/index.js +1 -0
- package/dist/types/core/messages/index.js +1 -0
- package/dist/types/core/messages/log.js +1 -0
- package/dist/types/core/parsing/babel-narrow.js +1 -0
- package/dist/types/core/parsing/index.js +1 -0
- package/dist/types/core/runtime.js +1 -0
- package/dist/types/guardian/index.js +2 -0
- package/dist/types/guardian/integrity.js +7 -0
- package/dist/types/guardian/snapshot.js +1 -0
- package/dist/types/index.js +4 -4
- package/dist/types/licenses/index.js +2 -0
- package/dist/types/processing/display.js +16 -13
- package/dist/types/processing/filters.js +3 -7
- package/dist/types/reports/compliance.js +1 -0
- package/dist/types/reports/streaming.js +1 -0
- package/dist/types/scripts/index.js +1 -0
- package/dist/types/scripts/migrar-aliases.js +1 -0
- package/dist/types/sdk/index.js +1 -0
- package/dist/types/shared/index.js +2 -0
- package/dist/types/vulnerabilities/index.js +2 -0
- package/dist/vulnerabilities/npm-audit.js +0 -1
- package/dist/vulnerabilities/scanner.js +2 -1
- package/dist/vulnerabilities/vulnerabilities.js +0 -1
- 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 =
|
|
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
|
|
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
|
|
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 =
|
|
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]
|
|
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 && (
|
|
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
|
-
|
|
144
|
-
|
|
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
|
|
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
|
|
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
|
|
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 (!
|
|
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:
|
|
14
|
+
return { func: fn, bodyBlock, isAnonymous, params, totalParams: fn.params.length };
|
|
15
15
|
};
|
|
16
|
-
if (textoTitulo.isFunctionDeclaration(node) &&
|
|
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)) &&
|
|
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 (
|
|
62
|
-
nome =
|
|
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' &&
|
|
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 (
|
|
145
|
-
comandoNome =
|
|
144
|
+
if (textoTitulo.isStringLiteral(primeiroArg)) {
|
|
145
|
+
comandoNome = primeiroArg.value;
|
|
146
146
|
}
|
|
147
|
-
const handler = call.arguments
|
|
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
|
|
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 (
|
|
184
|
-
nome =
|
|
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
|
|
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 (!
|
|
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 (
|
|
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 (
|
|
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
|
|
9
|
-
const LIMITE_PARAMETROS = config.ANALISE_LIMITES.FUNCOES_LONGAS.MAX_PARAMETROS
|
|
10
|
-
const LIMITE_ANINHAMENTO = config.ANALISE_LIMITES.FUNCOES_LONGAS.MAX_ANINHAMENTO
|
|
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 (
|
|
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 (
|
|
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
|
|
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 (
|
|
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
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
|
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' &&
|
|
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
|
-
|
|
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 (
|
|
42
|
+
if (textoTitulo.isObjectPattern(declarator.id)) {
|
|
43
43
|
for (const prop of declarator.id.properties) {
|
|
44
|
-
if (textoTitulo.isObjectProperty(prop) &&
|
|
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 (
|
|
149
|
+
if (textoTitulo.isMemberExpression(left) || textoTitulo.isIdentifier(left)) {
|
|
150
150
|
const right = path.node.right;
|
|
151
|
-
if (
|
|
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 (
|
|
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 (
|
|
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
|
|
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 (
|
|
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
|
|
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) {
|