@starci/eslint-canon-fe 8.0.4 → 8.0.6
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/CHANGELOG.md +8 -0
- package/package.json +1 -1
- package/runtime/knowledge/hfs/canon-pins.yaml +14 -7
- package/runtime/knowledge/hfs/slots.yaml +8 -8
- package/runtime/knowledge/sonar-gate.yaml +29 -6
- package/runtime/scripts/{lib/git.mjs → api/git/lib.mjs} +13 -14
- package/runtime/scripts/checks/architecture/dead-exports.mjs +32 -6
- package/runtime/scripts/checks/architecture/hfs.mjs +1 -1
- package/runtime/scripts/checks/architecture/required-files.mjs +1 -1
- package/runtime/scripts/lib/project-graph.mjs +0 -0
- package/runtime/scripts/lib/recorded-lines.mjs +1 -1
- package/runtime/scripts/lib/repo-identity.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 8.0.6 - 2026-10-01
|
|
4
|
+
|
|
5
|
+
- Changed: the runtime api layer move and the new pins.
|
|
6
|
+
|
|
7
|
+
## 8.0.5 - 2026-10-01
|
|
8
|
+
|
|
9
|
+
- Changed: bundled slots.yaml, sonar-gate.yaml and canon-pins.
|
|
10
|
+
|
|
3
11
|
## 8.0.4 - 2026-10-01
|
|
4
12
|
|
|
5
13
|
- Changed: the bundled runtime copy of knowledge/hfs/canon-pins.yaml pins @starci/test-world 1.0.3. No rule or command changed.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@starci/eslint-canon-fe",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.6",
|
|
4
4
|
"description": "The StarCi front-end lint canon: every rule its laws publish, gathered as one flat-config plugin and attached by one factory.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -16,6 +16,13 @@ purpose: >-
|
|
|
16
16
|
pins:
|
|
17
17
|
# --- @starci packages. Every @starci package is a PUBLISHED npm package (install: registry): a product repository
|
|
18
18
|
# installs the exact pinned version from the npm registry in the root and every workspace package.json, never `file:`.
|
|
19
|
+
'@starci/heroicons':
|
|
20
|
+
version: 0.3.1
|
|
21
|
+
group: starci
|
|
22
|
+
install: registry
|
|
23
|
+
side: fe
|
|
24
|
+
source: packages/heroicons/package.json
|
|
25
|
+
why: 'StarCi custom Heroicons-compatible cuts; eslint-canon-fe (icon.mjs) admits @starci/heroicons/24/outline and /16/solid as icon sources, so an app that uses them gets this exact version.'
|
|
19
26
|
'@starci/grammar':
|
|
20
27
|
version: 0.8.1
|
|
21
28
|
group: starci
|
|
@@ -24,14 +31,14 @@ pins:
|
|
|
24
31
|
source: packages/grammar/package.json
|
|
25
32
|
why: nivo-fe pins 0.4.11 and 0.5.0 in one workspace, starci-next-fe 0.5.1, miamia-fe 0.5.0; the runtime source is 0.8.0 (the brand layer sets `--font-sans` and `--font-mono`; the grammar reads them; 0.7.2 added the Input tel kind and IconButton disclosure props).
|
|
26
33
|
'@starci/eslint-canon-be':
|
|
27
|
-
version: 3.0.
|
|
34
|
+
version: 3.0.6
|
|
28
35
|
group: starci
|
|
29
36
|
install: registry
|
|
30
37
|
side: be
|
|
31
38
|
source: packages/eslint/be/package.json
|
|
32
39
|
why: '3.0.3: its bundled canon-pins copy pins stylelint-canon 2.0.2 and hfs 4.0.3; no rule changed. 3.0.2: its bundled runtime copies (slots, canon-pins, failure codes, the architecture machine) put .starcistacks and .sops.yaml at the app root (app.starcistacks, app.sops) and pin hfs 4.0.2. 3.0.1: its bundled canon-pins copy pins hfs 4.0.1. 3.0.0: `loadHfs(import.meta.url)` of be/eslint.config.mjs finds the app-root hfs.json (kind app) and gives every linted file the view of the be side; the project graph is built per side. 2.0.0 (C0 release): starciBeConfig({ hfs: loadHfs(import.meta.url) }) typed factory and the BE-CONVENTION laws.'
|
|
33
40
|
'@starci/eslint-canon-fe':
|
|
34
|
-
version: 8.0.
|
|
41
|
+
version: 8.0.6
|
|
35
42
|
group: starci
|
|
36
43
|
install: registry
|
|
37
44
|
side: fe
|
|
@@ -57,21 +64,21 @@ pins:
|
|
|
57
64
|
side: both
|
|
58
65
|
source: packages/prettier-config/package.json
|
|
59
66
|
'@starci/jest-preset':
|
|
60
|
-
version: 2.2.
|
|
67
|
+
version: 2.2.2
|
|
61
68
|
group: starci
|
|
62
69
|
install: registry
|
|
63
70
|
side: be
|
|
64
71
|
source: packages/jest-preset/package.json
|
|
65
|
-
why: '2.2.0: the integration, e2e and contract projects run every spec file in a worker process of its own (world-runner.cjs), so a process-global framework registry (@nestjs/graphql type metadata) never leaks between e2e files; 2.1.0: four projects on the test world, the unit kit (mockEntityManager, fakeTransaction, fakeIds, FakeClock, Outcome matchers) and the recordingOutbox claim side.'
|
|
72
|
+
why: '2.2.2: the unit run writes the lcov Sonar and Codecov import (services only); 2.2.0: the integration, e2e and contract projects run every spec file in a worker process of its own (world-runner.cjs), so a process-global framework registry (@nestjs/graphql type metadata) never leaks between e2e files; 2.1.0: four projects on the test world, the unit kit (mockEntityManager, fakeTransaction, fakeIds, FakeClock, Outcome matchers) and the recordingOutbox claim side.'
|
|
66
73
|
'@starci/test-world':
|
|
67
|
-
version: 1.0.
|
|
74
|
+
version: 1.0.5
|
|
68
75
|
group: starci
|
|
69
76
|
install: registry
|
|
70
77
|
side: be
|
|
71
78
|
source: packages/test-world/package.json
|
|
72
|
-
why: '1.0.3: the globalSetup registers the path aliases as TypeScript resolves them (the extends chain, paths from the config that declares them, the effective baseUrl), so a tests tsconfig that only extends the side config loads the declaration. 1.0.2: every path a declaration names (`stack`, seeds, the realm, Dockerfiles) resolves from the app root (the directory of hfs.json), where .starcistacks lives, never from the be side. The shared e2e library of every back end (R47, R48): the warm stack behind toxiproxy, the network-edge fakes, the Nest boot, the typed useTestWorld handle, useSandbox for contract specs, the outage lock and the starci-test-stack bin behind the managed test:stack script; a devDependency of every back end. 1.0.1: world.keycloak.events/sessions (the user events of the realm and live sessions through the admin API) and world.infra.postgresql.connection(name) (the database of one connection down while the others serve, under the outage lock).'
|
|
79
|
+
why: '1.0.5: a modules world boots real peer apps beside its modules, world.apps.<name>.during(fn) is the outage of a peer app, and w.keycloak.clientSecret(client) answers the run-generated secret of a confidential realm client. 1.0.4: world.resolve and scope.resolve take any Nest token (class, string or symbol). 1.0.3: the globalSetup registers the path aliases as TypeScript resolves them (the extends chain, paths from the config that declares them, the effective baseUrl), so a tests tsconfig that only extends the side config loads the declaration. 1.0.2: every path a declaration names (`stack`, seeds, the realm, Dockerfiles) resolves from the app root (the directory of hfs.json), where .starcistacks lives, never from the be side. The shared e2e library of every back end (R47, R48): the warm stack behind toxiproxy, the network-edge fakes, the Nest boot, the typed useTestWorld handle, useSandbox for contract specs, the outage lock and the starci-test-stack bin behind the managed test:stack script; a devDependency of every back end. 1.0.1: world.keycloak.events/sessions (the user events of the realm and live sessions through the admin API) and world.infra.postgresql.connection(name) (the database of one connection down while the others serve, under the outage lock).'
|
|
73
80
|
'@starci/hfs':
|
|
74
|
-
version: 4.0.
|
|
81
|
+
version: 4.0.6
|
|
75
82
|
group: starci
|
|
76
83
|
install: registry
|
|
77
84
|
side: both
|
|
@@ -246,7 +246,7 @@ slots:
|
|
|
246
246
|
rules: [HFS_TOOL_CONFIG_LOCAL]
|
|
247
247
|
- id: app.quality-config
|
|
248
248
|
profiles: [app]
|
|
249
|
-
path: sonar-project.properties
|
|
249
|
+
path: "{sonar-project.properties,codecov.yml}"
|
|
250
250
|
presence: required
|
|
251
251
|
tracked: tracked
|
|
252
252
|
tier: none
|
|
@@ -373,7 +373,7 @@ slots:
|
|
|
373
373
|
tracked: external
|
|
374
374
|
tier: none
|
|
375
375
|
tests: none
|
|
376
|
-
goesTo: "D:/starci-lanes/<project>/<lane>/ (outside every repository) for a lane; a
|
|
376
|
+
goesTo: "D:/starci-lanes/<project>/<lane>/ (outside every repository) for a lane; a Kernel workflow's worktree is created and owned by Orca outside the app checkout, never tracked, and removed by the runtime's host-side controller once the workflow's finish marked it release-pending"
|
|
377
377
|
- id: app.plaintext-env
|
|
378
378
|
profiles: [app]
|
|
379
379
|
path: "{.env,.env.*,.secrets/,**/*.pem,**/*.key}"
|
|
@@ -387,12 +387,12 @@ slots:
|
|
|
387
387
|
# ----- side root (both sides): what the old standalone repository root held, less the app-root files ----------
|
|
388
388
|
- id: repo.side-root-forbidden
|
|
389
389
|
profiles: [be, fe]
|
|
390
|
-
path: "{package.json,package-lock.json,hfs.json,README.md,.gitignore,.gitattributes,.husky/,.github/,.starciwork/,.starcistacks/,.sops.yaml,sonar-project.properties,.prettierrc,.prettierignore,scripts/}"
|
|
390
|
+
path: "{package.json,package-lock.json,hfs.json,README.md,.gitignore,.gitattributes,.husky/,.github/,.starciwork/,.starcistacks/,.sops.yaml,sonar-project.properties,codecov.yml,.prettierrc,.prettierignore,scripts/}"
|
|
391
391
|
presence: forbidden
|
|
392
392
|
tracked: external
|
|
393
393
|
tier: none
|
|
394
394
|
tests: none
|
|
395
|
-
goesTo: "the app root: one package.json, lockfile, hfs.json, README, git and CI files, hooks, formatter, Sonar configuration, scripts/, .starciwork, .starcistacks and .sops.yaml per app"
|
|
395
|
+
goesTo: "the app root: one package.json, lockfile, hfs.json, README, git and CI files, hooks, formatter, Sonar and Codecov configuration, scripts/, .starciwork, .starcistacks and .sops.yaml per app"
|
|
396
396
|
- id: be.tool-config
|
|
397
397
|
profiles: [be]
|
|
398
398
|
# Managed files (hfs sync renders them, hfs check compares them): the whole tool configuration of a back end. Each is
|
|
@@ -818,9 +818,9 @@ slots:
|
|
|
818
818
|
tier: integrations
|
|
819
819
|
owner: true
|
|
820
820
|
requires: [index.ts, "<provider>.config.ts"]
|
|
821
|
-
tests: unit-beside # <name>.service.spec.ts beside each <name>.service.ts
|
|
821
|
+
tests: unit-beside # <name>.service.spec.ts beside each <name>.service.ts; plus src/tests/integration/<provider>/ (R112)
|
|
822
822
|
budget: {indexExports: 60}
|
|
823
|
-
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT]
|
|
823
|
+
rules: [BE_TIER_DIRECTION, BE_ERROR_HOME, BE_SECRET_DEFAULT, BE_INTEGRATION_SPEC_MISSING]
|
|
824
824
|
- id: be.integrations.model
|
|
825
825
|
profiles: [be]
|
|
826
826
|
path: "src/modules/integrations/<provider>/model/"
|
|
@@ -897,8 +897,8 @@ slots:
|
|
|
897
897
|
tracked: tracked
|
|
898
898
|
tier: e2e
|
|
899
899
|
tests: e2e
|
|
900
|
-
why: one capability module
|
|
901
|
-
rules: [BE_TEST_TOPOLOGY]
|
|
900
|
+
why: one capability module through useTestWorld({ modules }), no HTTP door of ours (SQL, transactions, concurrency, inbox claims on the real database; and every integration client, `<capability>` being its provider folder, against our real infra, a real peer app or the library fake at the network edge, with its success, its ErrorCode refusal mapping and an outage through the world, R112); run by test:integration
|
|
901
|
+
rules: [BE_TEST_TOPOLOGY, BE_INTEGRATION_SPEC_MISSING]
|
|
902
902
|
- id: be.tests.e2e
|
|
903
903
|
profiles: [be]
|
|
904
904
|
path: "src/tests/e2e/<area>/*.e2e-spec.ts"
|
|
@@ -8,16 +8,24 @@ purpose: |
|
|
|
8
8
|
conditions and selects it for the project, and (2) judges an op's slice - the lines it changed - against
|
|
9
9
|
the same numbers. Code-writing ops cannot settle done while the slice is red
|
|
10
10
|
(scripts/kernel/sonar-settle.mjs). Changing a number here changes every repository on the next scan.
|
|
11
|
-
The gate has two parts: `newCode` (the recent work: duplication, blocker/critical issues, hotspots) and
|
|
12
|
-
`overall` (the whole code: no imported HFS, ESLint or stylelint finding, no
|
|
13
|
-
|
|
11
|
+
The gate has two parts: `newCode` (the recent work: coverage, duplication, blocker/critical issues, hotspots) and
|
|
12
|
+
`overall` (the whole code: coverage, no imported HFS, ESLint or stylelint finding, no bug, code smell or
|
|
13
|
+
vulnerability, every hotspot reviewed, no duplicated-lines excess, no cognitive-complexity issue). The one Sonar
|
|
14
|
+
mechanism of the HFS canon is packages/hfs/README.md section "Sonar".
|
|
14
15
|
gate:
|
|
15
16
|
name: starci-new-code
|
|
16
17
|
# A project with no new-code baseline judges the whole project as new code (nivo inc-f92febebbb64); a fixed
|
|
17
18
|
# window keeps "new code" the recent work on main, so the server gate is passable on a healthy main.
|
|
18
19
|
newCodePeriod: {type: NUMBER_OF_DAYS, value: 30}
|
|
19
20
|
newCode:
|
|
20
|
-
#
|
|
21
|
+
# Coverage of the services only. The be unit run writes coverage/lcov.info (@starci/jest-preset coverageReporters lcov) and the
|
|
22
|
+
# managed sonar-project.properties imports it (sonar.javascript.lcov.reportPaths=be/coverage/lcov.info) with
|
|
23
|
+
# sonar.coverage.inclusions=be/src/**/*.service.ts, the one scope rendered by hfs sync from the preset's COVERAGE_SOURCES (codecov.yml
|
|
24
|
+
# is rendered from the same constant). A handler, resolver, controller, module, config file or test is not a coverage target and
|
|
25
|
+
# fe/ is outside coverage entirely, so coverage on these conditions is the services' coverage alone.
|
|
26
|
+
coverage:
|
|
27
|
+
metric: new_coverage
|
|
28
|
+
minPercent: 100
|
|
21
29
|
# SonarQube's ignoreSmallChanges: fewer changed lines than this are not held to duplication.
|
|
22
30
|
ignoreBelowChangedLines: 20
|
|
23
31
|
duplication:
|
|
@@ -49,6 +57,19 @@ overall:
|
|
|
49
57
|
metric: violations
|
|
50
58
|
max: 0
|
|
51
59
|
engines: [starci-hfs, eslint, stylelint]
|
|
60
|
+
# The dashboard counts (`sonar-local dashboard`): each type of open issue, each held at `max`.
|
|
61
|
+
types: {bugs: BUG, code_smells: CODE_SMELL, vulnerabilities: VULNERABILITY}
|
|
62
|
+
# Every security hotspot of the whole code is reviewed (the dashboard's "hotspots reviewed").
|
|
63
|
+
hotspots:
|
|
64
|
+
metric: security_hotspots_reviewed
|
|
65
|
+
minReviewedPercent: 100
|
|
66
|
+
# The services' coverage (see newCode.coverage), overall and per file: with the inclusions in place each `*.service.ts` is its
|
|
67
|
+
# own measure, so one service below 100 fails the slice verdict (sonar-local evaluateSlice) and the dashboard verdict
|
|
68
|
+
# (sonar-local dashboard) although the project average may round to 100. A file outside the inclusions is never read.
|
|
69
|
+
coverage:
|
|
70
|
+
metric: coverage
|
|
71
|
+
minPercent: 100
|
|
72
|
+
perFile: true
|
|
52
73
|
# HFS_DUPLICATE_CODE (R21): duplicated lines density of the whole project, the Sonar enforcer of the machine's clone check.
|
|
53
74
|
# SonarJS runs its own token-based detection (sonar.cpd.* minimums are not read for TypeScript), so ruleParams.<profile>.duplicateBlock
|
|
54
75
|
# is enforced by the machine and imported as starci-hfs issues, and this density is the Sonar-side backstop.
|
|
@@ -70,8 +91,10 @@ enforcedOps: [backend.implement, interface.implement, code.refactor]
|
|
|
70
91
|
reasoning: |
|
|
71
92
|
Chosen against the four projects the local server holds on 2026-09-29 (whole-project duplicated lines
|
|
72
93
|
1.6 / 2.1 / 1.7 / 0.0 percent), so new code that meets them is at or below what main already carries.
|
|
73
|
-
|
|
74
|
-
imports
|
|
94
|
+
coverage 100 on services, overall and new code (owner 2026-10-01): the unit project already fails below its per-file
|
|
95
|
+
100 threshold on `*.service.ts`; Sonar imports the same run's lcov with the same scope, so the dashboard, the slice
|
|
96
|
+
verdict and the server gate show the number the runner enforces, file by file. Only services count: business logic
|
|
97
|
+
lives there, the thin layers are covered through them, and the front end has no unit tests.
|
|
75
98
|
duplication 3: the Sonar way value; the worst project today is 2.1, so 3 rejects a real copy-paste block and
|
|
76
99
|
passes ordinary work.
|
|
77
100
|
blocker/critical 0: the two severities that are defects, not style. main carries old debt of both kinds
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
// git.mjs — the one
|
|
1
|
+
// scripts/api/git/lib.mjs — the one place the runtime spawns git (scripts/checks/check-layers.mjs enforces it).
|
|
2
2
|
//
|
|
3
|
-
// Every
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
3
|
+
// Every caller spelt the same options by hand - encoding:'utf8', windowsHide:true, sometimes a timeout - with two shapes:
|
|
4
|
+
// `git args` in a cwd and `git -C dir args`. gitOutput is the throwing shape (stdout text, or an Error on a non-zero exit).
|
|
5
|
+
// gitSpawn keeps the spawnSync(file, args, options) signature so an injected runner or a spec's fake takes the same three
|
|
6
|
+
// arguments; runGit is the `-C` convenience; gitResult folds the result into the {ok, stdout, error} envelope; gitRunner
|
|
7
|
+
// folds any caller's runner into {ok, stdout, stderr}. The call files beside this one (worktree-*.mjs, rev-parse.mjs, ...)
|
|
8
|
+
// each name one git verb.
|
|
9
9
|
import { spawnSync } from 'node:child_process';
|
|
10
10
|
|
|
11
11
|
/**
|
|
@@ -43,11 +43,10 @@ export function gitResult(args, options = {}) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* a value not wrapped in quotes passes through unchanged.
|
|
46
|
+
* A caller's git runner folded to (args, opts) -> {ok, stdout, stderr}. `git`: the caller's runner (args, {cwd}) ->
|
|
47
|
+
* {ok|status, stdout|out, stderr|err} (a spec's fake); null: runGit with a 5-minute timeout and a 64 MB buffer.
|
|
49
48
|
*/
|
|
50
|
-
export const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
export const gitRunner = (git = null) => (args, opts = {}) => {
|
|
50
|
+
const r = git ? git(args, opts) : runGit(args, { timeout: 300_000, maxBuffer: 64 * 1024 * 1024, ...opts });
|
|
51
|
+
return { ok: r?.ok ?? (!r?.error && r?.status === 0), stdout: String(r?.stdout ?? r?.out ?? '').trim(), stderr: String(r?.stderr ?? r?.err ?? r?.error?.message ?? r?.error ?? '').trim() };
|
|
52
|
+
};
|
|
@@ -18,8 +18,12 @@
|
|
|
18
18
|
* used by the importers of that file; a re-exporting file nobody imports is a framework entry (a route file) and uses it.
|
|
19
19
|
* Specs and tests are not in the graph, so an export used only by a spec is dead, on purpose, with one exception (unit test
|
|
20
20
|
* standard): a `<name>.service.spec.ts` (the only unit spec kind) and a `*.builder.ts` under src/tests/fixtures/builders read from
|
|
21
|
-
* disk count as consumers, because a spec can only provide an Inject*() token or a param type the entry exports.
|
|
22
|
-
*
|
|
21
|
+
* disk count as consumers, because a spec can only provide an Inject*() token or a param type the entry exports. An integration
|
|
22
|
+
* spec (slot be.tests.integration, `src/tests/integration/<capability>/*.integration-spec.ts`) counts as a consumer of exactly one
|
|
23
|
+
* owner: the integration (slot be.integrations, `src/modules/integrations/<provider>/`) whose provider folder is its capability
|
|
24
|
+
* folder, because R112 makes that spec register the integration module and reference its ErrorCode enum. Specs of any other kind,
|
|
25
|
+
* an integration spec importing another owner, e2e specs and world files still do not count. A consumer inside the owner is
|
|
26
|
+
* skipped like any inside consumer.
|
|
23
27
|
*/
|
|
24
28
|
import fs from 'node:fs';
|
|
25
29
|
import { canonical } from './config.mjs';
|
|
@@ -168,13 +172,35 @@ function deadFiles(graph, config) {
|
|
|
168
172
|
}
|
|
169
173
|
|
|
170
174
|
const TEST_CONSUMER = /^(?:(?:src|apps)\/.+\.service\.spec\.ts|src\/tests\/fixtures\/builders\/.+\.builder\.ts)$/u;
|
|
175
|
+
/** The slot of integration specs and the slot of the integrations they pair with by folder (knowledge/hfs/slots.yaml). */
|
|
176
|
+
const INTEGRATION_SPEC_SLOT = 'be.tests.integration';
|
|
177
|
+
const INTEGRATION_SLOT = 'be.integrations';
|
|
171
178
|
|
|
172
|
-
/**
|
|
179
|
+
/** The provider an integration spec pairs with (its `<capability>` folder), or null when `rel` is no integration spec. */
|
|
180
|
+
function integrationSpecProvider(graph, rel) {
|
|
181
|
+
const classified = graph.resolver.classifyPath(rel);
|
|
182
|
+
return classified.slot === INTEGRATION_SPEC_SLOT && classified.status !== 'forbidden' ? classified.bindings?.capability ?? null : null;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Whether `to` belongs to the integration of `provider` (slot be.integrations, its `<provider>` binding). */
|
|
186
|
+
function inIntegrationOf(graph, to, provider) {
|
|
187
|
+
const owner = graph.resolver.ownerOf(to);
|
|
188
|
+
return owner?.slot === INTEGRATION_SLOT && owner.bindings?.provider === provider;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Pseudo edges (read from disk, outside the production program) to graph files: from the unit specs of services and the fixture
|
|
193
|
+
* builders to anything, and from an integration spec only to the integration of its own provider folder.
|
|
194
|
+
*/
|
|
173
195
|
function testConsumerEdges({ context, graph, config }) {
|
|
174
196
|
const { ts } = context;
|
|
175
197
|
const options = context.projects?.[0]?.options ?? {};
|
|
176
198
|
const edges = [];
|
|
177
|
-
|
|
199
|
+
const consumers = [...treeOf(config.root).files]
|
|
200
|
+
.map(file => ({ file, provider: TEST_CONSUMER.test(file) ? null : integrationSpecProvider(graph, file) }))
|
|
201
|
+
.filter(({ file, provider }) => provider !== null || TEST_CONSUMER.test(file))
|
|
202
|
+
.sort((a, b) => a.file.localeCompare(b.file));
|
|
203
|
+
for (const { file: rel, provider } of consumers) {
|
|
178
204
|
const abs = path.join(config.root, ...rel.split('/'));
|
|
179
205
|
let text;
|
|
180
206
|
try { text = fs.readFileSync(abs, 'utf8'); } catch { continue; }
|
|
@@ -183,7 +209,7 @@ function testConsumerEdges({ context, graph, config }) {
|
|
|
183
209
|
if (!(ts.isImportDeclaration(statement) || ts.isExportDeclaration(statement)) || !statement.moduleSpecifier || !ts.isStringLiteralLike(statement.moduleSpecifier)) continue;
|
|
184
210
|
const resolved = ts.resolveModuleName(statement.moduleSpecifier.text, abs, options, ts.sys).resolvedModule?.resolvedFileName;
|
|
185
211
|
const to = resolved ? graph.abs(canonical(resolved)) : null;
|
|
186
|
-
if (to) edges.push({ from: rel, to, edge: { declaration: statement }, reexport: false });
|
|
212
|
+
if (to && (provider === null || inIntegrationOf(graph, to, provider))) edges.push({ from: rel, to, edge: { declaration: statement }, reexport: false });
|
|
187
213
|
}
|
|
188
214
|
}
|
|
189
215
|
return edges;
|
|
@@ -254,7 +280,7 @@ export function checkDeadExports({ context, graph, config }) {
|
|
|
254
280
|
ruleId: 'HFS_UNUSED_EXPORT',
|
|
255
281
|
path: entry, line, column: 1,
|
|
256
282
|
name, owner: owner.root, slot: owner.slot,
|
|
257
|
-
message: `${entry} exports ${name}, but no production file outside ${owner.root || 'the repository root'} imports it; remove the export (only a service unit spec
|
|
283
|
+
message: `${entry} exports ${name}, but no production file outside ${owner.root || 'the repository root'} imports it; remove the export (only a service unit spec, a fixture builder, or an integration spec of this integration's own provider folder counts besides production files).`,
|
|
258
284
|
});
|
|
259
285
|
}
|
|
260
286
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { isIP } from 'node:net';
|
|
4
|
-
import { gitOutput } from '../../
|
|
4
|
+
import { gitOutput } from '../../api/git/lib.mjs';
|
|
5
5
|
import { repositoryName } from '../../lib/repo-identity.mjs';
|
|
6
6
|
import { braceVariants } from '../../lib/glob.mjs';
|
|
7
7
|
import { createSlotResolver, loadSlotManifest, openHfs } from '../../lib/hfs-slots.mjs';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { gitOutput } from '../../
|
|
3
|
+
import { gitOutput } from '../../api/git/lib.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* HFS check 5: the files and directories the slot manifest requires (knowledge/hfs/slots.yaml `requires`,
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// recorded-lines.mjs - the one home of the file-size ratchet's arithmetic (R20 HFS_SIZE_GROWTH), shared by the back-end
|
|
2
2
|
// and front-end lint canons (each ships a byte copy in its runtime/ bundle, kept by packages/hfs/scripts/sync-runtime.mjs).
|
|
3
3
|
import path from 'node:path';
|
|
4
|
-
import { runGit } from '
|
|
4
|
+
import { runGit } from '../api/git/lib.mjs';
|
|
5
5
|
|
|
6
6
|
/** The line count of a text; a trailing newline does not open a line. */
|
|
7
7
|
export const lineCount = (text) => {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// they carry the same README title, the same stack-declaration project name and the same sibling repositories.
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import path from 'node:path';
|
|
6
|
-
import { gitOutput } from '
|
|
6
|
+
import { gitOutput } from '../api/git/lib.mjs';
|
|
7
7
|
|
|
8
8
|
const git = (root, args) => gitOutput(args, { cwd: root }).trim();
|
|
9
9
|
const real = (p) => { try { return fs.realpathSync(p); } catch { return path.resolve(p); } };
|