@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.
Files changed (105) hide show
  1. package/README.md +2 -32
  2. package/dist/analysts/architects/analyst-structure.js +19 -19
  3. package/dist/analysts/architects/archetypes-custom.js +8 -6
  4. package/dist/analysts/architects/detector-archetypes.js +30 -27
  5. package/dist/analysts/architects/signals-project-advanced.js +24 -26
  6. package/dist/analysts/architects/signals-project.js +1 -1
  7. package/dist/analysts/architects/strategist-structure.js +47 -66
  8. package/dist/analysts/azure-pipelines/analysts/analyst-azure.js +21 -14
  9. package/dist/analysts/azure-pipelines/corrections/correction-azure.js +22 -19
  10. package/dist/analysts/azure-pipelines/detectors/detector-azure-performance.js +21 -18
  11. package/dist/analysts/azure-pipelines/detectors/detector-azure-security.js +25 -20
  12. package/dist/analysts/azure-pipelines/detectors/detector-azure-structure.js +39 -37
  13. package/dist/analysts/azure-pipelines/scorers/scorer-azure.js +10 -7
  14. package/dist/analysts/circleci/analysts/analyst-circleci.js +23 -15
  15. package/dist/analysts/circleci/corrections/correction-circleci.js +22 -20
  16. package/dist/analysts/circleci/detectors/detector-circleci-performance.js +23 -19
  17. package/dist/analysts/circleci/detectors/detector-circleci-security.js +78 -82
  18. package/dist/analysts/circleci/detectors/detector-circleci-structure.js +20 -17
  19. package/dist/analysts/converters/engines/converter-gha-to-gitlab.js +17 -15
  20. package/dist/analysts/corrections/analyst-quick-fixes.js +25 -28
  21. package/dist/analysts/corrections/analyst-scoring.js +24 -21
  22. package/dist/analysts/corrections/corrector-structure.js +35 -35
  23. package/dist/analysts/corrections/fix-alias-imports.js +10 -6
  24. package/dist/analysts/corrections/fix-md-fences.js +23 -23
  25. package/dist/analysts/corrections/map-reversion.js +54 -51
  26. package/dist/analysts/corrections/pruning.js +19 -16
  27. package/dist/analysts/corrections/quick-fixes/fix-any-to-proper-type.js +57 -61
  28. package/dist/analysts/corrections/quick-fixes/fix-unknown-to-specific-type.js +30 -30
  29. package/dist/analysts/corrections/reorganize-barrels.js +10 -10
  30. package/dist/analysts/corrections/scoring-shared.js +19 -18
  31. package/dist/analysts/corrections/type-safety/context-analyzer.js +15 -10
  32. package/dist/analysts/corrections/type-safety/type-validator.js +2 -1
  33. package/dist/analysts/corrections/type-safety/usage-analyzer.js +36 -39
  34. package/dist/analysts/css/analysts/analyst-css.js +16 -15
  35. package/dist/analysts/css/corrections/correction-css.js +19 -11
  36. package/dist/analysts/css/detectors/detector-css-best-practices.js +129 -118
  37. package/dist/analysts/css/scorers/scorer-css.js +23 -18
  38. package/dist/analysts/css-in-js/analysts/analyst-css-in-js.js +24 -17
  39. package/dist/analysts/css-in-js/corrections/correction-css-in-js.js +31 -16
  40. package/dist/analysts/css-in-js/detectors/detector-css-in-js-best-practices.js +20 -15
  41. package/dist/analysts/css-in-js/scorers/scorer-css-in-js.js +79 -54
  42. package/dist/analysts/css-in-js/shared.js +7 -0
  43. package/dist/analysts/detectors/detector-architecture.js +89 -83
  44. package/dist/analysts/detectors/detector-bugs-ml.js +21 -19
  45. package/dist/analysts/detectors/detector-code-fragile.js +30 -26
  46. package/dist/analysts/detectors/detector-duplications.js +21 -18
  47. package/dist/analysts/detectors/detector-monorepo.js +41 -36
  48. package/dist/analysts/detectors/detector-performance.js +22 -16
  49. package/dist/analysts/detectors/detector-recommendation-architecture.js +39 -35
  50. package/dist/analysts/detectors/detector-security.js +43 -42
  51. package/dist/analysts/detectors/detector-structure.js +29 -26
  52. package/dist/analysts/js-ts/detectors/detector-anti-patterns-async.js +4 -1
  53. package/dist/analysts/js-ts/detectors/detector-constructions-syntactic.js +10 -16
  54. package/dist/analysts/plugins/detector-documentation.js +1 -0
  55. package/dist/analysts/react/analysts/analyst-react-hooks.js +4 -2
  56. package/dist/analysts/react/analysts/analyst-react.js +1 -1
  57. package/dist/analysts/react/corrections/correction-react.js +2 -2
  58. package/dist/analysts/react/detectors/detector-react-best-practices.js +1 -1
  59. package/dist/analysts/registry/autodiscovery.js +2 -2
  60. package/dist/analysts/tailwind/corrections/correction-tailwind.js +0 -8
  61. package/dist/analysts/tailwind/scorers/scorer-tailwind.js +1 -2
  62. package/dist/cli/commands/command-analysts.js +3 -2
  63. package/dist/cli/commands/command-formatters.js +6 -5
  64. package/dist/cli/commands/command-github-actions.js +1 -1
  65. package/dist/cli/commands/command-guardian.js +1 -1
  66. package/dist/cli/commands/command-marketplace.js +4 -4
  67. package/dist/cli/diagnostic/filters.js +9 -9
  68. package/dist/core/config/chalk-safe.js +4 -4
  69. package/dist/core/config/config.js +5 -5
  70. package/dist/core/execution/analysis-cache.js +1 -1
  71. package/dist/core/execution/ast-cache.js +1 -3
  72. package/dist/core/execution/executor.js +2 -4
  73. package/dist/core/execution/scanner.js +1 -1
  74. package/dist/core/messages/en/analysts/report-circleci-messages.js +7 -7
  75. package/dist/core/messages/en/caretakers/caretaker-messages.js +1 -7
  76. package/dist/core/messages/ja/analysts/report-circleci-messages.js +7 -7
  77. package/dist/core/messages/ja/caretakers/caretaker-messages.js +1 -7
  78. package/dist/core/messages/pt/analysts/report-circleci-messages.js +7 -7
  79. package/dist/core/messages/pt/caretakers/caretaker-messages.js +1 -7
  80. package/dist/core/messages/zh/analysts/report-circleci-messages.js +7 -7
  81. package/dist/core/messages/zh/caretakers/caretaker-messages.js +1 -7
  82. package/dist/core/parsing/parser.js +2 -3
  83. package/dist/core/parsing/utils.js +1 -0
  84. package/dist/core/workers/worker-pool.js +0 -0
  85. package/dist/guardian/records.js +1 -0
  86. package/dist/guardian/sentinel.js +1 -0
  87. package/dist/guardian/watcher-hidden.js +1 -0
  88. package/dist/licenses/fs-utils.js +2 -1
  89. package/dist/licenses/header-options.js +1 -0
  90. package/dist/licenses/index.js +1 -1
  91. package/dist/reports/generator-report.js +3 -3
  92. package/dist/reports/report-patterns-usage.js +1 -1
  93. package/dist/shared/data-processing/fragment-report.js +1 -1
  94. package/dist/shared/formatters/engines/stylelint.js +40 -34
  95. package/dist/shared/formatters/formatters/markdown.js +1 -1
  96. package/dist/shared/helpers/framework-detector.js +5 -1
  97. package/dist/shared/persistence/persistence.js +13 -5
  98. package/dist/shared/plugins/core-plugin.js +3 -1
  99. package/dist/types/analysts/tailwind-correction.js +2 -0
  100. package/dist/types/index.js +3 -3
  101. package/dist/types/processing/filters.js +5 -4
  102. package/dist/vulnerabilities/npm-audit.js +1 -0
  103. package/dist/vulnerabilities/scanner.js +1 -1
  104. package/dist/vulnerabilities/vulnerabilities.js +1 -0
  105. 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
- if (sc.detected) {
107
- const first = sc.indices[0] ?? 0;
108
- ocorrencias.push(emitir(messages.CssInJsMensagens.detectedStyledComponents, relPath, messages.SeverityNiveis.info, findLine(src, first)));
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
- if (/createGlobalStyle/.test(linha)) {
23
- correcoes.push({
24
- tipo: 'css-in-js-extract-global',
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
- if (/!important/.test(linha) && /styled|css\s*`/.test(linha)) {
32
- correcoes.push({
33
- tipo: 'css-in-js-remove-important',
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 (/!important/.test(linha) && /styled|css\s*`|css\(/i.test(linha)) {
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
- if (/styled\s*\.\s*\w+\s*`[^`]*`/i.test(linha) || /css\s*`[^`]*`/i.test(linha)) {
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
- export function pontuarCssInJs(src, _relPath) {
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
- else {
25
- issues.push(m.globalStylesIssue);
26
- }
27
- const importantCount = (srcCompleto.match(/!important/g) ?? []).length;
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
- else {
33
- issues.push(m.importantUsageCount(importantCount));
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
- else {
41
- issues.push(m.httpUrlCount(httpUrls));
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
- else {
49
- issues.push(m.mixedStylingIssue);
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
- else {
57
- issues.push(m.noThemeSystem);
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
- else {
65
- issues.push('Estilos inline detectados - prefira styled components');
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
- else {
73
- issues.push(`${hasHardcodedBreakpoints} breakpoint(s) hardcoded`);
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
- else {
82
- issues.push('Elementos interativos sem pseudo-classes');
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
- else {
90
- issues.push(`${dynamicStyles} expressões dinâmicas - considere extração estática`);
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
- else if (STYLED_PATTERN.test(srcCompleto)) {
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
- else {
106
- issues.push(`${conditionalComplex} estilos condicionais complexos`);
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
- // Relatório de características
76
- if ((analiseCompleta.caracteristicas?.length ?? 0) > 0) {
77
- if (typeof contexto.report === 'function') {
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: 60
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
- // 2. Pattern-based bug detection
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
  };