energy-state-analyzer 0.10.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.markdownlintignore +9 -0
- package/AGENTS.md +26 -2
- package/CHANGELOG.md +26 -1
- package/Directory.Build.props +33 -0
- package/Directory.Build.targets +13 -0
- package/README.md +30 -11
- package/dist/cli.js +1 -1
- package/docs/cli.md +4 -4
- package/docs/configuration.md +106 -0
- package/docs/detectors/README.md +1 -0
- package/docs/detectors/file-coherence.md +15 -6
- package/docs/detectors/god-class.md +68 -0
- package/docs/detectors/parameter-explosion.md +164 -3
- package/package.json +48 -1
package/AGENTS.md
CHANGED
|
@@ -21,7 +21,9 @@ just watch # Fable and webpack watchers
|
|
|
21
21
|
just lint # Fantomas check over F# source and tests
|
|
22
22
|
just format # Format F# source and tests
|
|
23
23
|
just format-check # CI formatting check
|
|
24
|
+
just md-lint # Check markdown formatting (CI check)
|
|
24
25
|
just analyze # Build, then run the F# CLI against src/ or supplied paths
|
|
26
|
+
just fsharp-analyze# fsharp-analyzers (G-Research rules) over .fsproj; emits SARIF for CI
|
|
25
27
|
just test # Fable Scriptorium suite under Node
|
|
26
28
|
just pack # Production bundles + .vsix
|
|
27
29
|
just clean # Generated outputs and packages
|
|
@@ -36,7 +38,7 @@ webpack watch pipeline.
|
|
|
36
38
|
|
|
37
39
|
## Architecture
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
Code-quality parameters such as function and import counts are enforced by the analyzer, not by hand-written limits here — the analyzer is authoritative on them, so AGENTS.md does not set those numbers. If the analyzer flags a file, fix it (or challenge the analyzer if you believe its verdict is wrong); otherwise treat its output as final. `src/EnergyState.fsproj` has
|
|
40
42
|
`EnableDefaultCompileItems=false` behavior, so add every new `.fs` file explicitly in dependency
|
|
41
43
|
order.
|
|
42
44
|
|
|
@@ -78,9 +80,31 @@ compiles the library project for the extension entry, then the CLI project for `
|
|
|
78
80
|
The `.vsix` and npm package ship transpiled bundles, grammar WASMs, and metadata only. Keep F#
|
|
79
81
|
sources, tests, `fable-out/`, `fable_modules/`, `bin/`, `obj/`, and maps excluded by ignore files.
|
|
80
82
|
|
|
83
|
+
## F# analyzers (fsharp-analyzers)
|
|
84
|
+
|
|
85
|
+
Beyond our own product analyzer, the repo runs the [fsharp-analyzers](https://g-research.github.io/fsharp-analyzers/) rule set over our written F# via a dedicated CI job (`fsharp-analyzers` in `.github/workflows/ci.yml`). It is wired through MSBuild:
|
|
86
|
+
|
|
87
|
+
- `Directory.Build.props` adds `FSharp.Analyzers.Build` (the `AnalyzeFSharpProject` target) and `G-Research.FSharp.Analyzers` (the rules), and sets `RunAnalyzers=false` so these external rules never fire during Fable transpilation — the check is driven only through the explicit target.
|
|
88
|
+
- `Directory.Build.targets` sets `FSharpAnalyzersOtherFlags` (`--analyzers-path`, `--code-root`, `--report`). The rule package version in that path (currently `0.23.0`) must match the SDK the rules were built against; pair it with matching tooling versions in `.config/dotnet-tools.json` (`fsharp-analyzers`), or the CLI refuses to load the analyzer DLL on an SDK-version mismatch.
|
|
89
|
+
- `Directory.Build.*` are evaluated for every project, so their XML must be valid: **XML comments may not contain `--`** (write "the analyzers-path flag", not `--analyzers-path` inside a comment) — otherwise every project fails MSBuild evaluation and no SARIF is produced.
|
|
90
|
+
- The `fsharp-analyzers` dotnet tool (0.37.2) is restored by the existing `dotnet tool restore` step; run it locally with `just fsharp-analyze [paths]`.
|
|
91
|
+
|
|
92
|
+
The job is **soft-gated**: the `AnalyzeFSharpProject` target ignores the CLI exit code, so findings never fail the build, and a real restore/build/analysis error surfaces via the "Verify analyzer report exists" step (which fails loudly) instead of being hidden. A SARIF uploads per project to GitHub Code Scanning for triage over time. To make it blocking once the backlog clears, change the verify step to fail when results are non-empty.
|
|
93
|
+
|
|
94
|
+
**Default-branch scanning.** GitHub marks a tool as scanning the default branch only after it receives a SARIF upload tied to a commit *on that branch*. PR uploads populate just the PR-level view (the `?query=pr:N` Code Scanning filter); a successful **push to `main`** — which fires on every merge — is what populates the repository-wide alerts, so keep the job's `push: branches: [main]` trigger intact. The job also triggers on `pull_request` (PR-level view) and `workflow_dispatch` (manual scan), and declares `permissions: security_events: write` so those non-PR contexts can upload SARIF. That permission is required for the SARIF upload step; without it, push-to-`main` uploads fail even though analysis succeeds.
|
|
95
|
+
|
|
96
|
+
To force a scan of `main` on demand, run `just fsharp-scan-main` (or the equivalent `gh workflow trigger ci.yml --ref main`); it dispatches the CI job against `main` and uploads its SARIF. Use this if a push-to-`main` run fails — a failed upload leaves the default branch unscanned until one succeeds. Then check [Code Scanning](https://github.com/cardamomcode/energy-state-analyzer/security/code-scanning) for the default branch.
|
|
97
|
+
|
|
81
98
|
## Before committing or opening a PR
|
|
82
99
|
|
|
83
|
-
|
|
100
|
+
**Never commit directly to `main`.** Always create and work on a feature branch off `main`
|
|
101
|
+
(e.g. `fix/...`, `chore/...`, `ci/...`), open a PR from that branch targeting `main`, and never
|
|
102
|
+
force-push onto `main`. Keep `main` matching `origin/main`; if you accidentally commit to `main`,
|
|
103
|
+
reset it back to `origin/main` and carry the work on a feature branch instead. This keeps every
|
|
104
|
+
change reviewable through a PR and preserves ShipIt's release flow, which reads conventional
|
|
105
|
+
commit subjects from merged PRs.
|
|
106
|
+
|
|
107
|
+
Run `just format`, `just lint`, `just md-lint`, and `just analyze`. Triage every analyzer finding:
|
|
84
108
|
|
|
85
109
|
- **Real violation**: fix it.
|
|
86
110
|
- **Wrong implementation**: add a fixture and integration test, then fix the detector.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
last_commit_released:
|
|
2
|
+
last_commit_released: f21dcbf98bde0c68ffe935b91e74432e39c26977
|
|
3
3
|
name: energy-state-analyzer
|
|
4
4
|
updaters:
|
|
5
5
|
- command: npm version {version} --no-git-tag-version --allow-same-version
|
|
@@ -11,6 +11,31 @@ All notable changes to the "energy-state-analyzer" extension are generated by
|
|
|
11
11
|
[EasyBuild.ShipIt](https://github.com/easybuild-org/EasyBuild.ShipIt) from
|
|
12
12
|
[Conventional Commits](https://www.conventionalcommits.org/).
|
|
13
13
|
|
|
14
|
+
## 0.12.0 - 2026-09-04
|
|
15
|
+
|
|
16
|
+
### 🚀 Features
|
|
17
|
+
|
|
18
|
+
* Add per-detector enable/disable toggles and color settings (#90) ([f21dcbf](https://github.com/cardamomcode/energy-state-analyzer/commit/f21dcbf98bde0c68ffe935b91e74432e39c26977))
|
|
19
|
+
* *(core)* Add god-class detector to file-coherence check (#80) ([0da71cd](https://github.com/cardamomcode/energy-state-analyzer/commit/0da71cd1b518f1f2f8adea1e9f5613f5eddcbdc5))
|
|
20
|
+
|
|
21
|
+
### 🐞 Bug Fixes
|
|
22
|
+
|
|
23
|
+
* Resolve all fsharp-analyzers findings across F# source (#84) ([34892de](https://github.com/cardamomcode/energy-state-analyzer/commit/34892de4f18a9ae0564a004ed8668e74bcac5bd4))
|
|
24
|
+
* Reduce remaining analyzer findings in TypeCohesion and Python functions (#89) ([94f2c38](https://github.com/cardamomcode/energy-state-analyzer/commit/94f2c380df809a7972442883218dfca942f29587))
|
|
25
|
+
* *(analyzer)* Extract magic-number constants into named module-level bindings (#87) ([95b517c](https://github.com/cardamomcode/energy-state-analyzer/commit/95b517c0c559a3bb1753b3e4489672024d57e79e))
|
|
26
|
+
* *(ci)* Scan the default branch via schedule + manual trigger (#86) ([e213496](https://github.com/cardamomcode/energy-state-analyzer/commit/e2134966acb85beeb7bedd6cbd0b820d2e96e3b2))
|
|
27
|
+
|
|
28
|
+
<strong><small>[View changes on Github](https://github.com/cardamomcode/energy-state-analyzer/compare/7951a86146ecb0068b5d308f8a7b80134b819a4f..f21dcbf98bde0c68ffe935b91e74432e39c26977)</small></strong>
|
|
29
|
+
|
|
30
|
+
## 0.11.0 - 2026-09-03
|
|
31
|
+
|
|
32
|
+
### 🚀 Features
|
|
33
|
+
|
|
34
|
+
* Make Core.Config the single source of truth for thresholds and options (#74) ([42dcd1d](https://github.com/cardamomcode/energy-state-analyzer/commit/42dcd1d32c29a19fc16cba2c3d4860d0f42f74e3))
|
|
35
|
+
* *(core)* Make import-coherence thresholds configurable (#78) ([7951a86](https://github.com/cardamomcode/energy-state-analyzer/commit/7951a86146ecb0068b5d308f8a7b80134b819a4f))
|
|
36
|
+
|
|
37
|
+
<strong><small>[View changes on Github](https://github.com/cardamomcode/energy-state-analyzer/compare/b2795c89d9df95e46d26171a5393129c8f0208c3..7951a86146ecb0068b5d308f8a7b80134b819a4f)</small></strong>
|
|
38
|
+
|
|
14
39
|
## 0.10.1 - 2026-09-02
|
|
15
40
|
|
|
16
41
|
### 🐞 Bug Fixes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<Project>
|
|
2
|
+
<PropertyGroup>
|
|
3
|
+
<!--
|
|
4
|
+
F# analyzer (fsharp-analyzers) integration.
|
|
5
|
+
|
|
6
|
+
We run the fsharp-analyzers host explicitly via the AnalyzeFSharpProject target
|
|
7
|
+
(see Directory.Build.targets and `just fsharp-analyze`), not during a normal build.
|
|
8
|
+
Our production build is Fable transpilation; we do not want these external rules firing
|
|
9
|
+
while `dotnet fable` type-checks, so compiler-integrated analysis is disabled here and the
|
|
10
|
+
check is driven only through the explicit target in CI or on demand.
|
|
11
|
+
-->
|
|
12
|
+
<RunAnalyzers>false</RunAnalyzers>
|
|
13
|
+
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
|
|
14
|
+
<EnableDefaultAnalyzerOutputs>false</EnableDefaultAnalyzerOutputs>
|
|
15
|
+
</PropertyGroup>
|
|
16
|
+
|
|
17
|
+
<ItemGroup>
|
|
18
|
+
<!-- MSBuild hook: defines the AnalyzeFSharpProject / FSharpAnalyzerAfterBuild targets that
|
|
19
|
+
shell out to the `fsharp-analyzers` CLI. Restored as a normal package (not an analyzer
|
|
20
|
+
asset) because it contributes build targets, not compiler plugins. -->
|
|
21
|
+
<PackageReference Include="FSharp.Analyzers.Build" Version="0.5.0">
|
|
22
|
+
<PrivateAssets>all</PrivateAssets>
|
|
23
|
+
</PackageReference>
|
|
24
|
+
<!-- The rule set (G-Research F# checker rules). Referenced as an analyzer asset so NuGet
|
|
25
|
+
extracts the DLL to disk; the CLI locates it via the analyzers-path flag (using $(
|
|
26
|
+
NuGetPackageRoot)) in Directory.Build.targets. RunAnalyzers=false above keeps it from
|
|
27
|
+
running at build time. -->
|
|
28
|
+
<PackageReference Include="G-Research.FSharp.Analyzers" Version="0.23.0">
|
|
29
|
+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
30
|
+
<PrivateAssets>all</PrivateAssets>
|
|
31
|
+
</PackageReference>
|
|
32
|
+
</ItemGroup>
|
|
33
|
+
</Project>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<Project>
|
|
2
|
+
<PropertyGroup>
|
|
3
|
+
<!--
|
|
4
|
+
CLI arguments for the fsharp-analyzers host, consumed by the AnalyzeFSharpProject target.
|
|
5
|
+
Kept on ONE line: <Exec Command="..."> is passed to sh verbatim, so any newline in the
|
|
6
|
+
value would be treated as a separate command. analyzers-path points at the G-Research rule
|
|
7
|
+
DLL extracted from its NuGet package (resolved per machine via $(NuGetPackageRoot));
|
|
8
|
+
code-root makes SARIF positions relative to the repo root; report writes one SARIF per
|
|
9
|
+
project under fsharp-analyzer-reports/ (git-ignored) for CI code-scanning upload.
|
|
10
|
+
-->
|
|
11
|
+
<FSharpAnalyzersOtherFlags>--analyzers-path "$(NuGetPackageRoot)g-research.fsharp.analyzers/0.23.0/analyzers/dotnet/fs" --code-root "$(MSBuildProjectDirectory)/.." --report "$(MSBuildProjectDirectory)/../fsharp-analyzer-reports/$(MSBuildProjectName).sarif"</FSharpAnalyzersOtherFlags>
|
|
12
|
+
</PropertyGroup>
|
|
13
|
+
</Project>
|
package/README.md
CHANGED
|
@@ -64,22 +64,41 @@ for the Extension Development Host.
|
|
|
64
64
|
|
|
65
65
|
## Extension Settings
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
-
|
|
72
|
-
|
|
73
|
-
- `energyStateAnalyzer.
|
|
74
|
-
- `energyStateAnalyzer.
|
|
67
|
+
Settings split into two concerns: **which detectors run and how they look** live in VS Code (editor-only toggles and colors), while **how strict each detector is** lives in a project `.esaconfig.json` shared with the CLI/CI.
|
|
68
|
+
|
|
69
|
+
### Enable/disable detectors and pick colors (VS Code settings)
|
|
70
|
+
|
|
71
|
+
Every detector has an `enabled` toggle, plus the magic-number/string switches and the color palette. All toggles default to `true`. See each detector's doc (linked under Features above) for what it flags:
|
|
72
|
+
|
|
73
|
+
- `energyStateAnalyzer.nesting.enabled` (`true`)
|
|
74
|
+
- `energyStateAnalyzer.cyclomaticComplexity.enabled` (`true`)
|
|
75
|
+
- `energyStateAnalyzer.cognitiveComplexity.enabled` (`true`)
|
|
76
|
+
- `energyStateAnalyzer.coherence.enabled` (`true`)
|
|
77
|
+
- `energyStateAnalyzer.matchOpportunity.enabled` (`true`)
|
|
78
|
+
- `energyStateAnalyzer.parameterCount.enabled` (`true`)
|
|
79
|
+
- `energyStateAnalyzer.primitiveObsession.enabled` (`true`)
|
|
80
|
+
- `energyStateAnalyzer.opaqueBoolean.enabled` (`true`)
|
|
81
|
+
- `energyStateAnalyzer.logicalControlFlow.enabled` (`true`)
|
|
82
|
+
- `energyStateAnalyzer.inversion.enabled` (`true`)
|
|
75
83
|
- `energyStateAnalyzer.magicNumber.enabled` (`true`)
|
|
76
|
-
- `energyStateAnalyzer.magicNumber.allowlist` (`[0, 1, -1, 2]`)
|
|
77
84
|
- `energyStateAnalyzer.magicString.enabled` (`true`)
|
|
78
|
-
- `energyStateAnalyzer.magicString.minDuplicates` (`2`)
|
|
79
|
-
- `energyStateAnalyzer.magicString.allowlist` (`["", "utf-8", "__main__"]`)
|
|
80
85
|
- `energyStateAnalyzer.colors.highEnergy` / `.mediumEnergy` / `.lowEnergy` (`#fb8500` / `#ffb703` / `#99dd99`)
|
|
81
86
|
- `energyStateAnalyzer.colors.backgroundOpacity` (`0.1`)
|
|
82
87
|
|
|
88
|
+
### Thresholds and allowlists (`.esaconfig.json`)
|
|
89
|
+
|
|
90
|
+
Set thresholds, ratios, and magic-number/string allowlists in an `.esaconfig.json` file to share them between the editor and CLI/CI — see [docs/configuration.md](docs/configuration.md) for the schema, per-key defaults, and how the file layers over VS Code settings (`defaults < .esaconfig.json < host override`). Thresholds are **not** configurable in VS Code on purpose: a project decides how strict its detectors are, and that decision travels with the repo. The keys (all optional; an absent key keeps its default) include:
|
|
91
|
+
|
|
92
|
+
- `nesting.mediumThreshold` / `highThreshold` (`3` / `5`)
|
|
93
|
+
- `cognitiveComplexity.mediumThreshold` / `highThreshold` (`15` / `25`)
|
|
94
|
+
- `coherence.largeFunctionLines` (`20`), `maxLargeFunctions` (`5`), `singleDomainNameShare` (`0.7`)
|
|
95
|
+
- `matchOpportunity.minBranches` (`3`)
|
|
96
|
+
- `parameterCount.mediumThreshold` / `highThreshold` (`5` / `8`)
|
|
97
|
+
- `magicNumber.allowlist` (`[0, 1, -1, 2]`)
|
|
98
|
+
- `magicString.minDuplicates` (`2`), `allowlist` (`["", "utf-8", "__main__"]`)
|
|
99
|
+
|
|
100
|
+
The magic-number/string `enabled` toggles above remain in VS Code — enabling or disabling a detector is an editor-only convenience, so it stays out of the shared project file.
|
|
101
|
+
|
|
83
102
|
Changes take effect immediately on the active editor.
|
|
84
103
|
|
|
85
104
|
To exclude files/folders (e.g. test fixtures, generated code) from both the extension's live analysis and the CLI, add a `.esaignore` file to your workspace root — see [`docs/cli.md`](docs/cli.md#excluding-files-and-folders-esaignore).
|