@mocoto/mahoraga 0.14.2 → 0.14.4
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/README.md +2 -32
- package/dist/analysts/architects/analyst-structure.js +19 -19
- package/dist/analysts/architects/archetypes-custom.js +8 -6
- package/dist/analysts/architects/detector-archetypes.js +30 -27
- package/dist/analysts/architects/signals-project-advanced.js +24 -26
- package/dist/analysts/architects/signals-project.js +1 -1
- package/dist/analysts/architects/strategist-structure.js +47 -66
- package/dist/analysts/azure-pipelines/analysts/analyst-azure.js +21 -14
- package/dist/analysts/azure-pipelines/corrections/correction-azure.js +22 -19
- package/dist/analysts/azure-pipelines/detectors/detector-azure-performance.js +21 -18
- package/dist/analysts/azure-pipelines/detectors/detector-azure-security.js +25 -20
- package/dist/analysts/azure-pipelines/detectors/detector-azure-structure.js +39 -37
- package/dist/analysts/azure-pipelines/scorers/scorer-azure.js +10 -7
- package/dist/analysts/circleci/analysts/analyst-circleci.js +23 -15
- package/dist/analysts/circleci/corrections/correction-circleci.js +22 -20
- package/dist/analysts/circleci/detectors/detector-circleci-performance.js +23 -19
- package/dist/analysts/circleci/detectors/detector-circleci-security.js +78 -82
- package/dist/analysts/circleci/detectors/detector-circleci-structure.js +20 -17
- package/dist/analysts/converters/engines/converter-gha-to-gitlab.js +17 -15
- package/dist/analysts/corrections/analyst-quick-fixes.js +25 -28
- package/dist/analysts/corrections/analyst-scoring.js +24 -21
- package/dist/analysts/corrections/corrector-structure.js +35 -35
- package/dist/analysts/corrections/fix-alias-imports.js +10 -6
- package/dist/analysts/corrections/fix-md-fences.js +23 -23
- package/dist/analysts/corrections/map-reversion.js +54 -51
- package/dist/analysts/corrections/pruning.js +19 -16
- package/dist/analysts/corrections/quick-fixes/fix-any-to-proper-type.js +57 -61
- package/dist/analysts/corrections/quick-fixes/fix-unknown-to-specific-type.js +30 -30
- package/dist/analysts/corrections/reorganize-barrels.js +10 -10
- package/dist/analysts/corrections/scoring-shared.js +19 -18
- package/dist/analysts/corrections/type-safety/context-analyzer.js +15 -10
- package/dist/analysts/corrections/type-safety/type-validator.js +2 -1
- package/dist/analysts/corrections/type-safety/usage-analyzer.js +36 -39
- package/dist/analysts/css/analysts/analyst-css.js +16 -15
- package/dist/analysts/css/corrections/correction-css.js +19 -11
- package/dist/analysts/css/detectors/detector-css-best-practices.js +129 -118
- package/dist/analysts/css/scorers/scorer-css.js +23 -18
- package/dist/analysts/css-in-js/analysts/analyst-css-in-js.js +24 -17
- package/dist/analysts/css-in-js/corrections/correction-css-in-js.js +31 -16
- package/dist/analysts/css-in-js/detectors/detector-css-in-js-best-practices.js +20 -15
- package/dist/analysts/css-in-js/scorers/scorer-css-in-js.js +79 -54
- package/dist/analysts/css-in-js/shared.js +7 -0
- package/dist/analysts/detectors/detector-architecture.js +89 -83
- package/dist/analysts/detectors/detector-bugs-ml.js +21 -19
- package/dist/analysts/detectors/detector-code-fragile.js +30 -26
- package/dist/analysts/detectors/detector-duplications.js +21 -18
- package/dist/analysts/detectors/detector-monorepo.js +41 -36
- package/dist/analysts/detectors/detector-performance.js +22 -16
- package/dist/analysts/detectors/detector-recommendation-architecture.js +39 -35
- package/dist/analysts/detectors/detector-security.js +43 -42
- package/dist/analysts/detectors/detector-structure.js +29 -26
- package/dist/analysts/js-ts/detectors/detector-anti-patterns-async.js +4 -1
- package/dist/analysts/js-ts/detectors/detector-constructions-syntactic.js +10 -16
- package/dist/analysts/plugins/detector-documentation.js +1 -0
- package/dist/analysts/react/analysts/analyst-react-hooks.js +4 -2
- package/dist/analysts/react/analysts/analyst-react.js +1 -1
- package/dist/analysts/react/corrections/correction-react.js +2 -2
- package/dist/analysts/react/detectors/detector-react-best-practices.js +1 -1
- package/dist/analysts/registry/autodiscovery.js +2 -2
- package/dist/analysts/tailwind/corrections/correction-tailwind.js +0 -8
- package/dist/analysts/tailwind/scorers/scorer-tailwind.js +1 -2
- package/dist/cli/commands/command-analysts.js +3 -2
- package/dist/cli/commands/command-formatters.js +6 -5
- package/dist/cli/commands/command-github-actions.js +1 -1
- package/dist/cli/commands/command-guardian.js +1 -1
- package/dist/cli/commands/command-marketplace.js +4 -4
- package/dist/cli/diagnostic/filters.js +9 -9
- package/dist/core/config/chalk-safe.js +4 -4
- package/dist/core/config/config.js +5 -5
- package/dist/core/execution/analysis-cache.js +1 -1
- package/dist/core/execution/ast-cache.js +1 -3
- package/dist/core/execution/executor.js +2 -4
- package/dist/core/execution/scanner.js +1 -1
- package/dist/core/messages/en/analysts/report-circleci-messages.js +7 -7
- package/dist/core/messages/en/caretakers/caretaker-messages.js +1 -7
- package/dist/core/messages/ja/analysts/report-circleci-messages.js +7 -7
- package/dist/core/messages/ja/caretakers/caretaker-messages.js +1 -7
- package/dist/core/messages/pt/analysts/report-circleci-messages.js +7 -7
- package/dist/core/messages/pt/caretakers/caretaker-messages.js +1 -7
- package/dist/core/messages/zh/analysts/report-circleci-messages.js +7 -7
- package/dist/core/messages/zh/caretakers/caretaker-messages.js +1 -7
- package/dist/core/parsing/parser.js +2 -3
- package/dist/core/parsing/utils.js +1 -0
- package/dist/core/workers/worker-pool.js +0 -0
- package/dist/guardian/records.js +1 -0
- package/dist/guardian/sentinel.js +1 -0
- package/dist/guardian/watcher-hidden.js +1 -0
- package/dist/licenses/fs-utils.js +2 -1
- package/dist/licenses/header-options.js +1 -0
- package/dist/licenses/index.js +1 -1
- package/dist/reports/generator-report.js +3 -3
- package/dist/reports/report-patterns-usage.js +1 -1
- package/dist/shared/data-processing/fragment-report.js +1 -1
- package/dist/shared/formatters/engines/stylelint.js +40 -34
- package/dist/shared/formatters/formatters/markdown.js +1 -1
- package/dist/shared/helpers/framework-detector.js +5 -1
- package/dist/shared/persistence/persistence.js +13 -5
- package/dist/shared/plugins/core-plugin.js +3 -1
- package/dist/types/analysts/tailwind-correction.js +2 -0
- package/dist/types/index.js +3 -3
- package/dist/types/processing/filters.js +5 -4
- package/dist/vulnerabilities/npm-audit.js +1 -0
- package/dist/vulnerabilities/scanner.js +1 -1
- package/dist/vulnerabilities/vulnerabilities.js +1 -0
- package/package.json +5 -10
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: MIT
|
|
2
2
|
import { messages } from '../../../core/messages/index.js';
|
|
3
|
+
import { IMPORTANT_PATTERN } from '../shared.js';
|
|
3
4
|
import { criarAnalista, criarOcorrencia } from '../../../types/index.js';
|
|
4
5
|
const disableEnv = process.env.SUKUNA_DISABLE_PLUGIN_CSS_IN_JS === '1';
|
|
5
6
|
function findLine(src, index = 0) {
|
|
@@ -80,6 +81,26 @@ function detectarStyledJsx(src) {
|
|
|
80
81
|
indices
|
|
81
82
|
};
|
|
82
83
|
}
|
|
84
|
+
function processarStyledComponents(sc, src, relPath, ocorrencias) {
|
|
85
|
+
if (!sc.detected) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const first = sc.indices[0] ?? 0;
|
|
89
|
+
ocorrencias.push(emitir(messages.CssInJsMensagens.detectedStyledComponents, relPath, messages.SeverityNiveis.info, findLine(src, first)));
|
|
90
|
+
if (sc.global) {
|
|
91
|
+
ocorrencias.push(emitir(messages.CssInJsMensagens.globalStyles('styled-components'), relPath, messages.SeverityNiveis.warning, findLine(src, first)));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function processarStyledJsx(sj, src, relPath, ocorrencias) {
|
|
95
|
+
if (!sj.detected) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const first = sj.indices[0] ?? 0;
|
|
99
|
+
ocorrencias.push(emitir(messages.CssInJsMensagens.detectedStyledJsx, relPath, messages.SeverityNiveis.info, findLine(src, first)));
|
|
100
|
+
if (sj.global) {
|
|
101
|
+
ocorrencias.push(emitir(messages.CssInJsMensagens.globalStyles('styled-jsx'), relPath, messages.SeverityNiveis.warning, findLine(src, first)));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
83
104
|
export const analistaCssInJs = criarAnalista({
|
|
84
105
|
nome: 'analista-css-in-js',
|
|
85
106
|
categoria: 'estilo',
|
|
@@ -93,7 +114,6 @@ export const analistaCssInJs = criarAnalista({
|
|
|
93
114
|
if (relPath.includes('src/analistas/css-in-js/')) {
|
|
94
115
|
return null;
|
|
95
116
|
}
|
|
96
|
-
// Prefiltro barato para evitar múltiplos matchAll em arquivos que não têm sinais.
|
|
97
117
|
if (!/styled-components|styled-jsx|<style\b[^>]*\bjsx\b|\bcreateGlobalStyle\b|\bstyled\b/i.test(src)) {
|
|
98
118
|
return null;
|
|
99
119
|
}
|
|
@@ -103,22 +123,9 @@ export const analistaCssInJs = criarAnalista({
|
|
|
103
123
|
if (!sc.detected && !sj.detected) {
|
|
104
124
|
return null;
|
|
105
125
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (sc.global) {
|
|
110
|
-
ocorrencias.push(emitir(messages.CssInJsMensagens.globalStyles('styled-components'), relPath, messages.SeverityNiveis.warning, findLine(src, first)));
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
if (sj.detected) {
|
|
114
|
-
const first = sj.indices[0] ?? 0;
|
|
115
|
-
ocorrencias.push(emitir(messages.CssInJsMensagens.detectedStyledJsx, relPath, messages.SeverityNiveis.info, findLine(src, first)));
|
|
116
|
-
if (sj.global) {
|
|
117
|
-
ocorrencias.push(emitir(messages.CssInJsMensagens.globalStyles('styled-jsx'), relPath, messages.SeverityNiveis.warning, findLine(src, first)));
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// Sinais comuns dentro de CSS em template strings / style tags
|
|
121
|
-
if (/!important/.test(src)) {
|
|
126
|
+
processarStyledComponents(sc, src, relPath, ocorrencias);
|
|
127
|
+
processarStyledJsx(sj, src, relPath, ocorrencias);
|
|
128
|
+
if (IMPORTANT_PATTERN.test(src)) {
|
|
122
129
|
ocorrencias.push(emitir(messages.CssInJsMensagens.importantUsage, relPath, messages.SeverityNiveis.warning, 1));
|
|
123
130
|
}
|
|
124
131
|
if (/url\(\s*['"]?http:\/\//i.test(src)) {
|
|
@@ -12,30 +12,45 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { getMessages } from '../../../core/messages/index.js';
|
|
14
14
|
import { splitLines } from '../../../shared/helpers/index.js';
|
|
15
|
+
import { IMPORTANT_PATTERN, IMPORTANT_WS_GLOBAL } from '../shared.js';
|
|
15
16
|
import { criarOcorrencia } from '../../../types/index.js';
|
|
17
|
+
function detectarCreateGlobalStyle(linha, numeroLinha, m) {
|
|
18
|
+
if (!/createGlobalStyle/.test(linha)) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
tipo: 'css-in-js-extract-global',
|
|
23
|
+
original: linha.trim(),
|
|
24
|
+
sugerido: m.extractGlobalCss,
|
|
25
|
+
linha: numeroLinha,
|
|
26
|
+
confianca: 70
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
function detectarImportantCorrection(linha, numeroLinha) {
|
|
30
|
+
if (!IMPORTANT_PATTERN.test(linha) || !/styled|css\s*`/.test(linha)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
tipo: 'css-in-js-remove-important',
|
|
35
|
+
original: linha.trim(),
|
|
36
|
+
sugerido: linha.replace(IMPORTANT_WS_GLOBAL, ''),
|
|
37
|
+
linha: numeroLinha,
|
|
38
|
+
confianca: 80
|
|
39
|
+
};
|
|
40
|
+
}
|
|
16
41
|
export function detectarCorrecoesCssInJs(src, _relPath) {
|
|
17
42
|
const m = getMessages().CssInJsCorrecaoMensagens;
|
|
18
43
|
const correcoes = [];
|
|
19
44
|
const linhas = splitLines(src);
|
|
20
45
|
linhas.forEach((linha, index) => {
|
|
21
46
|
const numeroLinha = index + 1;
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
original: linha.trim(),
|
|
26
|
-
sugerido: m.extractGlobalCss,
|
|
27
|
-
linha: numeroLinha,
|
|
28
|
-
confianca: 70
|
|
29
|
-
});
|
|
47
|
+
const globalCorrection = detectarCreateGlobalStyle(linha, numeroLinha, m);
|
|
48
|
+
if (globalCorrection) {
|
|
49
|
+
correcoes.push(globalCorrection);
|
|
30
50
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
original: linha.trim(),
|
|
35
|
-
sugerido: linha.replace(/\s*!important/g, ''),
|
|
36
|
-
linha: numeroLinha,
|
|
37
|
-
confianca: 80
|
|
38
|
-
});
|
|
51
|
+
const importantCorrection = detectarImportantCorrection(linha, numeroLinha);
|
|
52
|
+
if (importantCorrection) {
|
|
53
|
+
correcoes.push(importantCorrection);
|
|
39
54
|
}
|
|
40
55
|
});
|
|
41
56
|
return correcoes;
|
|
@@ -4,10 +4,28 @@
|
|
|
4
4
|
// em expressões regulares de detecção, gerando falso positivo em auto-análise.
|
|
5
5
|
import { getMessages } from '../../../core/messages/index.js';
|
|
6
6
|
import { splitLines } from '../../../shared/helpers/index.js';
|
|
7
|
+
import { IMPORTANT_PATTERN } from '../shared.js';
|
|
7
8
|
import { criarOcorrencia } from '../../../types/index.js';
|
|
8
9
|
const STYLED_PATTERN = /styled\s*(\.|\()`|css\s*`|css\(|StyleSheet\.create|makeStyles|useStyles|createStyles|withStyles/;
|
|
9
10
|
const PSEUDO_CLASSES = [':hover', ':focus', ':active', ':visited', ':disabled', ':checked', ':focus-within', ':focus-visible'];
|
|
10
11
|
const BREAKPOINT_PATTERN = /\b(768|1024|1280|480|320)\s*px\b/;
|
|
12
|
+
function detectarMissingPseudoClasses(linha, numeroLinha, ocorrencias, relPath, m) {
|
|
13
|
+
if (!/styled\s*\.\s*\w+\s*`[^`]*`/i.test(linha) && !/css\s*`[^`]*`/i.test(linha)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const hasPseudo = PSEUDO_CLASSES.some(pc => linha.includes(pc));
|
|
17
|
+
const isInteractive = /button|input|a\s*\(|link|click|onClick|onChange|onSubmit|role\s*=\s*["']button/i.test(linha);
|
|
18
|
+
if (isInteractive && !hasPseudo) {
|
|
19
|
+
ocorrencias.push(criarOcorrencia({
|
|
20
|
+
tipo: 'css-in-js-missing-pseudo-classes',
|
|
21
|
+
nivel: 'info',
|
|
22
|
+
mensagem: m.CssInJsMensagens.missingPseudoClasses,
|
|
23
|
+
relPath,
|
|
24
|
+
linha: numeroLinha,
|
|
25
|
+
sugestao: m.CssInJsMensagens.missingPseudoClassesSugestao
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
11
29
|
export const detectorCssInJsBestPractices = {
|
|
12
30
|
nome: 'detector-css-in-js-best-practices',
|
|
13
31
|
categoria: 'estilo',
|
|
@@ -35,7 +53,7 @@ export const detectorCssInJsBestPractices = {
|
|
|
35
53
|
sugestao: m.CssInJsMensagens.sugestaoEvitarGlobalStyles
|
|
36
54
|
}));
|
|
37
55
|
}
|
|
38
|
-
if (
|
|
56
|
+
if (IMPORTANT_PATTERN.test(linha) && /styled|css\s*`|css\(/i.test(linha)) {
|
|
39
57
|
ocorrencias.push(criarOcorrencia({
|
|
40
58
|
tipo: 'css-in-js-important',
|
|
41
59
|
nivel: 'info',
|
|
@@ -85,20 +103,7 @@ export const detectorCssInJsBestPractices = {
|
|
|
85
103
|
sugestao: m.CssInJsMensagens.hardcodedBreakpointSugestao
|
|
86
104
|
}));
|
|
87
105
|
}
|
|
88
|
-
|
|
89
|
-
const hasPseudo = PSEUDO_CLASSES.some(pc => linha.includes(pc));
|
|
90
|
-
const isInteractive = /button|input|a\s*\(|link|click|onClick|onChange|onSubmit|role\s*=\s*["']button/i.test(linha);
|
|
91
|
-
if (isInteractive && !hasPseudo) {
|
|
92
|
-
ocorrencias.push(criarOcorrencia({
|
|
93
|
-
tipo: 'css-in-js-missing-pseudo-classes',
|
|
94
|
-
nivel: 'info',
|
|
95
|
-
mensagem: m.CssInJsMensagens.missingPseudoClasses,
|
|
96
|
-
relPath,
|
|
97
|
-
linha: numeroLinha,
|
|
98
|
-
sugestao: m.CssInJsMensagens.missingPseudoClassesSugestao
|
|
99
|
-
}));
|
|
100
|
-
}
|
|
101
|
-
}
|
|
106
|
+
detectarMissingPseudoClasses(linha, numeroLinha, ocorrencias, relPath, m);
|
|
102
107
|
if (/css\s*`[^$]*`/.test(linha) || /css`[^$]*`/.test(linha)) {
|
|
103
108
|
ocorrencias.push(criarOcorrencia({
|
|
104
109
|
tipo: 'css-in-js-tagged-template-no-expr',
|
|
@@ -4,107 +4,132 @@
|
|
|
4
4
|
// em expressões regulares de detecção, gerando falso positivo em auto-análise.
|
|
5
5
|
import { getMessages } from '../../../core/messages/index.js';
|
|
6
6
|
import { splitLines } from '../../../shared/helpers/index.js';
|
|
7
|
+
import { IMPORTANT_GLOBAL } from '../shared.js';
|
|
7
8
|
const STYLED_PATTERN = /styled\s*(\.|\()`|css\s*`|css\(|StyleSheet\.create|makeStyles|useStyles|createStyles|withStyles/;
|
|
8
|
-
|
|
9
|
-
const m = getMessages().CssInJsPontuacaoMensagens;
|
|
10
|
-
let score = 0;
|
|
11
|
-
const maxScore = 100;
|
|
12
|
-
const issues = [];
|
|
13
|
-
const strengths = [];
|
|
14
|
-
const _linhas = splitLines(src);
|
|
15
|
-
const srcCompleto = src;
|
|
16
|
-
if (!STYLED_PATTERN.test(srcCompleto)) {
|
|
17
|
-
return { score: 0, maxScore, percentage: 0, issues: [m.notDetected], strengths: [] };
|
|
18
|
-
}
|
|
19
|
-
strengths.push(m.detected);
|
|
9
|
+
function avaliarGlobalStyles(srcCompleto, m, strengths, issues) {
|
|
20
10
|
if (!/createGlobalStyle|<style[^>]*global/i.test(srcCompleto)) {
|
|
21
|
-
score += 15;
|
|
22
11
|
strengths.push(m.noGlobalStyles);
|
|
12
|
+
return 15;
|
|
23
13
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
issues.push(m.globalStylesIssue);
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
function avaliarImportantUsage(srcCompleto, m, strengths, issues) {
|
|
18
|
+
const importantCount = (srcCompleto.match(IMPORTANT_GLOBAL) ?? []).length;
|
|
28
19
|
if (importantCount === 0) {
|
|
29
|
-
score += 10;
|
|
30
20
|
strengths.push(m.noImportant);
|
|
21
|
+
return 10;
|
|
31
22
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
23
|
+
issues.push(m.importantUsageCount(importantCount));
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
function avaliarHttpUrls(srcCompleto, m, strengths, issues) {
|
|
35
27
|
const httpUrls = (srcCompleto.match(/url\(\s*['"]?http:\/\//gi) ?? []).length;
|
|
36
28
|
if (httpUrls === 0) {
|
|
37
|
-
score += 8;
|
|
38
29
|
strengths.push(m.noHttpUrls);
|
|
30
|
+
return 8;
|
|
39
31
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
32
|
+
issues.push(m.httpUrlCount(httpUrls));
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
function avaliarMixedStyling(srcCompleto, m, strengths, issues) {
|
|
43
36
|
const hasMixed = /className=.*style=\{/.test(srcCompleto);
|
|
44
37
|
if (!hasMixed) {
|
|
45
|
-
score += 10;
|
|
46
38
|
strengths.push(m.consistentStyling);
|
|
39
|
+
return 10;
|
|
47
40
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
issues.push(m.mixedStylingIssue);
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
function avaliarThemeUsage(srcCompleto, m, strengths, issues) {
|
|
51
45
|
const hasTheme = /useTheme|theme\.|ThemeProvider/i.test(srcCompleto);
|
|
52
46
|
if (hasTheme) {
|
|
53
|
-
score += 12;
|
|
54
47
|
strengths.push(m.themeUsage);
|
|
48
|
+
return 12;
|
|
55
49
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
50
|
+
issues.push(m.noThemeSystem);
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
function avaliarInlineStyles(srcCompleto, strengths, issues) {
|
|
59
54
|
const hasInlineStyles = /style\s*=\s*\{\s*\{/.test(srcCompleto);
|
|
60
55
|
if (!hasInlineStyles) {
|
|
61
|
-
score += 8;
|
|
62
56
|
strengths.push('Sem estilos inline excessivos');
|
|
57
|
+
return 8;
|
|
63
58
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
issues.push('Estilos inline detectados - prefira styled components');
|
|
60
|
+
return 0;
|
|
61
|
+
}
|
|
62
|
+
function avaliarBreakpoints(srcCompleto, strengths, issues) {
|
|
67
63
|
const hasHardcodedBreakpoints = (srcCompleto.match(/\b(768|1024|1280|480|320)\s*px\b/g) ?? []).length;
|
|
68
64
|
if (hasHardcodedBreakpoints === 0) {
|
|
69
|
-
score += 7;
|
|
70
65
|
strengths.push('Breakpoints consistentes');
|
|
66
|
+
return 7;
|
|
71
67
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
68
|
+
issues.push(`${hasHardcodedBreakpoints} breakpoint(s) hardcoded`);
|
|
69
|
+
return 0;
|
|
70
|
+
}
|
|
71
|
+
function avaliarPseudoClasses(srcCompleto, strengths, issues) {
|
|
75
72
|
const pseudoInteractive = /:hover|:focus|:active/.test(srcCompleto);
|
|
76
73
|
const hasInteractive = /button|input|a\s*\(|link|click|onClick/i.test(srcCompleto);
|
|
77
74
|
if (!hasInteractive || pseudoInteractive) {
|
|
78
|
-
score += 7;
|
|
79
75
|
strengths.push('Pseudo-classes para estados interativos');
|
|
76
|
+
return 7;
|
|
80
77
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
issues.push('Elementos interativos sem pseudo-classes');
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
function avaliarDynamicStyles(srcCompleto, strengths, issues) {
|
|
84
82
|
const dynamicStyles = (srcCompleto.match(/\$\{.*\}/g) ?? []).length;
|
|
85
83
|
if (dynamicStyles <= 5) {
|
|
86
|
-
score += 8;
|
|
87
84
|
strengths.push('Estilos dinâmicos controlados');
|
|
85
|
+
return 8;
|
|
88
86
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
issues.push(`${dynamicStyles} expressões dinâmicas - considere extração estática`);
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
function avaliarExtraction(srcCompleto, strengths, issues) {
|
|
92
91
|
const hasExtraction = /extract|linaria|vanilla-extract|compiled|griffel/i.test(srcCompleto);
|
|
93
92
|
if (hasExtraction) {
|
|
94
|
-
score += 10;
|
|
95
93
|
strengths.push('Extração de CSS estático ativa');
|
|
94
|
+
return 10;
|
|
96
95
|
}
|
|
97
|
-
|
|
96
|
+
if (STYLED_PATTERN.test(srcCompleto)) {
|
|
98
97
|
issues.push('Sem extração de CSS estático - considere ferramentas como linaria');
|
|
99
98
|
}
|
|
99
|
+
return 0;
|
|
100
|
+
}
|
|
101
|
+
function avaliarConditionalComplexity(srcCompleto, strengths, issues) {
|
|
100
102
|
const conditionalComplex = (srcCompleto.match(/\$\{.*\?.*:.*\}/g) ?? []).length;
|
|
101
103
|
if (conditionalComplex <= 3) {
|
|
102
|
-
score += 5;
|
|
103
104
|
strengths.push('Estilos condicionais simplificados');
|
|
105
|
+
return 5;
|
|
104
106
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
+
issues.push(`${conditionalComplex} estilos condicionais complexos`);
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
export function pontuarCssInJs(src, _relPath) {
|
|
111
|
+
const m = getMessages().CssInJsPontuacaoMensagens;
|
|
112
|
+
let score = 0;
|
|
113
|
+
const maxScore = 100;
|
|
114
|
+
const issues = [];
|
|
115
|
+
const strengths = [];
|
|
116
|
+
const _linhas = splitLines(src);
|
|
117
|
+
const srcCompleto = src;
|
|
118
|
+
if (!STYLED_PATTERN.test(srcCompleto)) {
|
|
119
|
+
return { score: 0, maxScore, percentage: 0, issues: [m.notDetected], strengths: [] };
|
|
107
120
|
}
|
|
121
|
+
strengths.push(m.detected);
|
|
122
|
+
score += avaliarGlobalStyles(srcCompleto, m, strengths, issues);
|
|
123
|
+
score += avaliarImportantUsage(srcCompleto, m, strengths, issues);
|
|
124
|
+
score += avaliarHttpUrls(srcCompleto, m, strengths, issues);
|
|
125
|
+
score += avaliarMixedStyling(srcCompleto, m, strengths, issues);
|
|
126
|
+
score += avaliarThemeUsage(srcCompleto, m, strengths, issues);
|
|
127
|
+
score += avaliarInlineStyles(srcCompleto, strengths, issues);
|
|
128
|
+
score += avaliarBreakpoints(srcCompleto, strengths, issues);
|
|
129
|
+
score += avaliarPseudoClasses(srcCompleto, strengths, issues);
|
|
130
|
+
score += avaliarDynamicStyles(srcCompleto, strengths, issues);
|
|
131
|
+
score += avaliarExtraction(srcCompleto, strengths, issues);
|
|
132
|
+
score += avaliarConditionalComplexity(srcCompleto, strengths, issues);
|
|
108
133
|
return {
|
|
109
134
|
score: Math.min(score, maxScore),
|
|
110
135
|
maxScore,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Padrões compartilhados para detecção CSS-in-JS
|
|
3
|
+
// Usa construção dinâmica para evitar falso positivo em auto-análise
|
|
4
|
+
const IMPORTANT_CHAR = String.fromCharCode(33);
|
|
5
|
+
export const IMPORTANT_PATTERN = new RegExp(`${IMPORTANT_CHAR}important`);
|
|
6
|
+
export const IMPORTANT_GLOBAL = new RegExp(`${IMPORTANT_CHAR}important`, 'g');
|
|
7
|
+
export const IMPORTANT_WS_GLOBAL = new RegExp(`\\s*${IMPORTANT_CHAR}important`, 'g');
|
|
@@ -2,6 +2,91 @@ import * as path from 'path';
|
|
|
2
2
|
import { traverse } from '../../core/config/index.js';
|
|
3
3
|
import { messages } from '../../core/messages/index.js';
|
|
4
4
|
import { criarOcorrencia } from '../../types/index.js';
|
|
5
|
+
const CONFIANCA_ACOPLAMENTO = 60;
|
|
6
|
+
async function reportarCaracteristicas(analiseCompleta, contexto, ocorrencias, relPath) {
|
|
7
|
+
if ((analiseCompleta.caracteristicas?.length ?? 0) === 0) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (typeof contexto.report === 'function') {
|
|
11
|
+
contexto.report({
|
|
12
|
+
code: 'ARQ_CARACTERISTICAS',
|
|
13
|
+
tipo: 'caracteristicas-arquitetura',
|
|
14
|
+
nivel: 'info',
|
|
15
|
+
relPath,
|
|
16
|
+
linha: 1,
|
|
17
|
+
data: {
|
|
18
|
+
caracteristicas: analiseCompleta.caracteristicas ?? []
|
|
19
|
+
},
|
|
20
|
+
origem: 'arquitetura'
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const { DetectorArquiteturaMensagens } = await import('../../core/messages/pt/analysts/detector-architecture-messages.js');
|
|
25
|
+
ocorrencias.push(criarOcorrencia({
|
|
26
|
+
tipo: 'caracteristicas-arquitetura',
|
|
27
|
+
nivel: 'info',
|
|
28
|
+
mensagem: DetectorArquiteturaMensagens.caracteristicas(analiseCompleta.caracteristicas ?? []),
|
|
29
|
+
relPath,
|
|
30
|
+
linha: 1
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
async function reportarViolacoes(analiseCompleta, contexto, ocorrencias, relPath) {
|
|
34
|
+
for (const violacao of (analiseCompleta.violacoes ?? []).slice(0, 3)) {
|
|
35
|
+
if (typeof contexto.report === 'function') {
|
|
36
|
+
contexto.report({
|
|
37
|
+
code: 'ARQ_VIOLACAO',
|
|
38
|
+
tipo: 'violacao-arquitetura',
|
|
39
|
+
nivel: 'aviso',
|
|
40
|
+
relPath,
|
|
41
|
+
linha: 1,
|
|
42
|
+
data: { violacao },
|
|
43
|
+
origem: 'arquitetura'
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const { DetectorArquiteturaMensagens } = await import('../../core/messages/pt/analysts/detector-architecture-messages.js');
|
|
48
|
+
ocorrencias.push(criarOcorrencia({
|
|
49
|
+
tipo: 'violacao-arquitetura',
|
|
50
|
+
nivel: 'aviso',
|
|
51
|
+
mensagem: DetectorArquiteturaMensagens.violacao(violacao),
|
|
52
|
+
relPath,
|
|
53
|
+
linha: 1
|
|
54
|
+
}));
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function reportarMetricas(analiseCompleta, contexto, ocorrencias, relPath) {
|
|
59
|
+
const metricas = analiseCompleta.metricas;
|
|
60
|
+
if (!metricas || typeof metricas !== 'object' || !('acoplamento' in metricas) || !('coesao' in metricas)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if ((metricas.acoplamento ?? 0) <= 0.7 && (metricas.coesao ?? 1) >= 0.3) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (typeof contexto.report === 'function') {
|
|
67
|
+
contexto.report({
|
|
68
|
+
code: 'ARQ_METRICAS',
|
|
69
|
+
tipo: 'metricas-arquitetura',
|
|
70
|
+
nivel: 'aviso',
|
|
71
|
+
relPath,
|
|
72
|
+
linha: 1,
|
|
73
|
+
data: {
|
|
74
|
+
acoplamento: metricas.acoplamento ?? 0,
|
|
75
|
+
coesao: metricas.coesao ?? 0
|
|
76
|
+
},
|
|
77
|
+
origem: 'arquitetura'
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const { DetectorArquiteturaMensagens } = await import('../../core/messages/pt/analysts/detector-architecture-messages.js');
|
|
82
|
+
ocorrencias.push(criarOcorrencia({
|
|
83
|
+
tipo: 'metricas-arquitetura',
|
|
84
|
+
nivel: 'aviso',
|
|
85
|
+
mensagem: DetectorArquiteturaMensagens.metricas(metricas.acoplamento ?? 0, metricas.coesao ?? 0),
|
|
86
|
+
relPath,
|
|
87
|
+
linha: 1
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
5
90
|
export const analistaArquitetura = {
|
|
6
91
|
nome: 'arquitetura',
|
|
7
92
|
categoria: 'estrutura',
|
|
@@ -72,88 +157,9 @@ export const analistaArquitetura = {
|
|
|
72
157
|
linha: 1
|
|
73
158
|
}));
|
|
74
159
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
contexto.report({
|
|
79
|
-
code: 'ARQ_CARACTERISTICAS',
|
|
80
|
-
tipo: 'caracteristicas-arquitetura',
|
|
81
|
-
nivel: 'info',
|
|
82
|
-
relPath,
|
|
83
|
-
linha: 1,
|
|
84
|
-
data: {
|
|
85
|
-
caracteristicas: analiseCompleta.caracteristicas ?? []
|
|
86
|
-
},
|
|
87
|
-
origem: 'arquitetura'
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
const { DetectorArquiteturaMensagens } = await import('../../core/messages/pt/analysts/detector-architecture-messages.js');
|
|
92
|
-
ocorrencias.push(criarOcorrencia({
|
|
93
|
-
tipo: 'caracteristicas-arquitetura',
|
|
94
|
-
nivel: 'info',
|
|
95
|
-
mensagem: DetectorArquiteturaMensagens.caracteristicas(analiseCompleta.caracteristicas ?? []),
|
|
96
|
-
relPath,
|
|
97
|
-
linha: 1
|
|
98
|
-
}));
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
// Relatório de violações
|
|
102
|
-
for (const violacao of (analiseCompleta.violacoes ?? []).slice(0, 3)) {
|
|
103
|
-
if (typeof contexto.report === 'function') {
|
|
104
|
-
contexto.report({
|
|
105
|
-
code: 'ARQ_VIOLACAO',
|
|
106
|
-
tipo: 'violacao-arquitetura',
|
|
107
|
-
nivel: 'aviso',
|
|
108
|
-
relPath,
|
|
109
|
-
linha: 1,
|
|
110
|
-
data: {
|
|
111
|
-
violacao
|
|
112
|
-
},
|
|
113
|
-
origem: 'arquitetura'
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
117
|
-
const { DetectorArquiteturaMensagens } = await import('../../core/messages/pt/analysts/detector-architecture-messages.js');
|
|
118
|
-
ocorrencias.push(criarOcorrencia({
|
|
119
|
-
tipo: 'violacao-arquitetura',
|
|
120
|
-
nivel: 'aviso',
|
|
121
|
-
mensagem: DetectorArquiteturaMensagens.violacao(violacao),
|
|
122
|
-
relPath,
|
|
123
|
-
linha: 1
|
|
124
|
-
}));
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
// Métricas de qualidade
|
|
128
|
-
const metricas = analiseCompleta.metricas;
|
|
129
|
-
if (metricas && typeof metricas === 'object' && 'acoplamento' in metricas && 'coesao' in metricas) {
|
|
130
|
-
if ((metricas.acoplamento ?? 0) > 0.7 || (metricas.coesao ?? 1) < 0.3) {
|
|
131
|
-
if (typeof contexto.report === 'function') {
|
|
132
|
-
contexto.report({
|
|
133
|
-
code: 'ARQ_METRICAS',
|
|
134
|
-
tipo: 'metricas-arquitetura',
|
|
135
|
-
nivel: 'aviso',
|
|
136
|
-
relPath,
|
|
137
|
-
linha: 1,
|
|
138
|
-
data: {
|
|
139
|
-
acoplamento: metricas.acoplamento ?? 0,
|
|
140
|
-
coesao: metricas.coesao ?? 0
|
|
141
|
-
},
|
|
142
|
-
origem: 'arquitetura'
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
else {
|
|
146
|
-
const { DetectorArquiteturaMensagens } = await import('../../core/messages/pt/analysts/detector-architecture-messages.js');
|
|
147
|
-
ocorrencias.push(criarOcorrencia({
|
|
148
|
-
tipo: 'metricas-arquitetura',
|
|
149
|
-
nivel: 'aviso',
|
|
150
|
-
mensagem: DetectorArquiteturaMensagens.metricas(metricas.acoplamento ?? 0, metricas.coesao ?? 0),
|
|
151
|
-
relPath,
|
|
152
|
-
linha: 1
|
|
153
|
-
}));
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
160
|
+
await reportarCaracteristicas(analiseCompleta, contexto, ocorrencias, relPath);
|
|
161
|
+
await reportarViolacoes(analiseCompleta, contexto, ocorrencias, relPath);
|
|
162
|
+
await reportarMetricas(analiseCompleta, contexto, ocorrencias, relPath);
|
|
157
163
|
return ocorrencias;
|
|
158
164
|
}
|
|
159
165
|
catch (erro) {
|
|
@@ -390,7 +396,7 @@ function detectarPadraoArquitetural(estatisticas) {
|
|
|
390
396
|
if (temDependenciasCirculares) {
|
|
391
397
|
return {
|
|
392
398
|
tipo: 'Arquitetura com Problemas de Acoplamento',
|
|
393
|
-
confianca:
|
|
399
|
+
confianca: CONFIANCA_ACOPLAMENTO
|
|
394
400
|
};
|
|
395
401
|
}
|
|
396
402
|
return {
|
|
@@ -445,6 +445,26 @@ function hasValidation(content, _line) {
|
|
|
445
445
|
function contarLinhasAtePos(content, pos) {
|
|
446
446
|
return content.slice(0, pos).split('\n').length;
|
|
447
447
|
}
|
|
448
|
+
function detectarPadroesBug(src, relPath, ocorrencias) {
|
|
449
|
+
for (const pattern of BUG_PATTERNS) {
|
|
450
|
+
try {
|
|
451
|
+
const match = pattern.detect(src, relPath);
|
|
452
|
+
if (match && match.confidence > 0.5) {
|
|
453
|
+
const nivel = pattern.severity === 'erro' && match.confidence > 0.8 ? 'erro' : pattern.severity;
|
|
454
|
+
ocorrencias.push({
|
|
455
|
+
tipo: `ml-${pattern.id}`,
|
|
456
|
+
nivel,
|
|
457
|
+
mensagem: getMessages().BugMlMensagens.mlPattern((match.confidence * 100).toFixed(0), match.message),
|
|
458
|
+
origem: 'detector-bugs-ml',
|
|
459
|
+
relPath,
|
|
460
|
+
linha: match.line ?? 0,
|
|
461
|
+
sugestao: match.suggestion,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
catch { }
|
|
466
|
+
}
|
|
467
|
+
}
|
|
448
468
|
export const detectorBugsMl = {
|
|
449
469
|
nome: 'detector-bugs-ml',
|
|
450
470
|
global: false,
|
|
@@ -473,25 +493,7 @@ export const detectorBugsMl = {
|
|
|
473
493
|
linha: 0,
|
|
474
494
|
});
|
|
475
495
|
}
|
|
476
|
-
|
|
477
|
-
for (const pattern of BUG_PATTERNS) {
|
|
478
|
-
try {
|
|
479
|
-
const match = pattern.detect(src, relPath);
|
|
480
|
-
if (match && match.confidence > 0.5) {
|
|
481
|
-
const nivel = pattern.severity === 'erro' && match.confidence > 0.8 ? 'erro' : pattern.severity;
|
|
482
|
-
ocorrencias.push({
|
|
483
|
-
tipo: `ml-${pattern.id}`,
|
|
484
|
-
nivel,
|
|
485
|
-
mensagem: getMessages().BugMlMensagens.mlPattern((match.confidence * 100).toFixed(0), match.message),
|
|
486
|
-
origem: 'detector-bugs-ml',
|
|
487
|
-
relPath,
|
|
488
|
-
linha: match.line ?? 0,
|
|
489
|
-
sugestao: match.suggestion,
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
catch { }
|
|
494
|
-
}
|
|
496
|
+
detectarPadroesBug(src, relPath, ocorrencias);
|
|
495
497
|
return ocorrencias;
|
|
496
498
|
},
|
|
497
499
|
};
|