@memberjunction/standards 0.0.0 → 6.1.0-edge.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.
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @fileoverview The standards registry.
3
+ *
4
+ * Adding a check here makes it **available**, not active. No repo's result changes until a human
5
+ * adopts it — that is the property that lets this package ship new standards continuously instead
6
+ * of in scary batches.
7
+ *
8
+ * @module @memberjunction/standards
9
+ */
10
+ import type { StandardCheck } from './types.js';
11
+ /**
12
+ * Every registered standard.
13
+ *
14
+ * **Adding a check:** append it with a `Since` equal to the MJ version it will ship in. Never
15
+ * backdate `Since` — that would silently activate the check in repos that adopted before it
16
+ * existed, which is exactly what this design prevents.
17
+ *
18
+ * **Removing a check:** don't, without a deprecation cycle. A repo whose config names a check that
19
+ * no longer exists gets an "unknown check" warning rather than a silent no-op, so removal is
20
+ * visible — but it is still a breaking change for anyone whose CI names it.
21
+ */
22
+ export declare const STANDARD_CHECKS: readonly StandardCheck[];
23
+ /** Look up a check by its config key. */
24
+ export declare function GetCheck(id: string): StandardCheck | undefined;
25
+ /** All registered check ids, sorted — used by `adopt` and by unknown-key reporting. */
26
+ export declare function AllCheckIds(): string[];
27
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,aAAa,EAAoB,CAAC;AAEzE,yCAAyC;AACzC,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAE9D;AAED,uFAAuF;AACvF,wBAAgB,WAAW,IAAI,MAAM,EAAE,CAEtC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @fileoverview The standards registry.
3
+ *
4
+ * Adding a check here makes it **available**, not active. No repo's result changes until a human
5
+ * adopts it — that is the property that lets this package ship new standards continuously instead
6
+ * of in scary batches.
7
+ *
8
+ * @module @memberjunction/standards
9
+ */
10
+ import { UILayersCheck } from './checks/ui-layers.js';
11
+ /**
12
+ * Every registered standard.
13
+ *
14
+ * **Adding a check:** append it with a `Since` equal to the MJ version it will ship in. Never
15
+ * backdate `Since` — that would silently activate the check in repos that adopted before it
16
+ * existed, which is exactly what this design prevents.
17
+ *
18
+ * **Removing a check:** don't, without a deprecation cycle. A repo whose config names a check that
19
+ * no longer exists gets an "unknown check" warning rather than a silent no-op, so removal is
20
+ * visible — but it is still a breaking change for anyone whose CI names it.
21
+ */
22
+ export const STANDARD_CHECKS = [UILayersCheck];
23
+ /** Look up a check by its config key. */
24
+ export function GetCheck(id) {
25
+ return STANDARD_CHECKS.find((c) => c.Id === id);
26
+ }
27
+ /** All registered check ids, sorted — used by `adopt` and by unknown-key reporting. */
28
+ export function AllCheckIds() {
29
+ return STANDARD_CHECKS.map((c) => c.Id).sort();
30
+ }
31
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,eAAe,GAA6B,CAAC,aAAa,CAAC,CAAC;AAEzE,yCAAyC;AACzC,MAAM,UAAU,QAAQ,CAAC,EAAU;IAC/B,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;AACpD,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,WAAW;IACvB,OAAO,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AACnD,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @fileoverview Running the adopted standards and formatting the result.
3
+ *
4
+ * @module @memberjunction/standards
5
+ */
6
+ import type { RunSummary, StandardsConfig } from './types.js';
7
+ /**
8
+ * Run every standard this repo has adopted.
9
+ *
10
+ * **Only adopted checks run.** A registered check absent from the config is reported as available
11
+ * and not executed — which is what makes it safe to add standards to this package at any time.
12
+ * A check present but `off` is likewise not executed, but is not reported as available either:
13
+ * the repo has seen it and said no.
14
+ */
15
+ export declare function RunStandards(repoRoot: string, config: StandardsConfig): Promise<RunSummary>;
16
+ /**
17
+ * Render a run for a terminal.
18
+ *
19
+ * Every failing check prints its docs URL. A rule whose reasoning is one search away gets followed;
20
+ * one that just says "no" gets worked around.
21
+ */
22
+ export declare function FormatSummary(summary: RunSummary, config: StandardsConfig): string;
23
+ /** Exit code for a run: non-zero only for `error`-severity violations. */
24
+ export declare function ExitCodeFor(summary: RunSummary): number;
25
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAgB,UAAU,EAAE,eAAe,EAAa,MAAM,YAAY,CAAC;AAGvF;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CAmCjG;AAOD;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,eAAe,GAAG,MAAM,CA4ClF;AAID,0EAA0E;AAC1E,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,GAAG,MAAM,CAEvD"}
package/dist/runner.js ADDED
@@ -0,0 +1,110 @@
1
+ /**
2
+ * @fileoverview Running the adopted standards and formatting the result.
3
+ *
4
+ * @module @memberjunction/standards
5
+ */
6
+ import { GetCheck, STANDARD_CHECKS } from './registry.js';
7
+ import { IsNewerThan } from './version.js';
8
+ /**
9
+ * Run every standard this repo has adopted.
10
+ *
11
+ * **Only adopted checks run.** A registered check absent from the config is reported as available
12
+ * and not executed — which is what makes it safe to add standards to this package at any time.
13
+ * A check present but `off` is likewise not executed, but is not reported as available either:
14
+ * the repo has seen it and said no.
15
+ */
16
+ export async function RunStandards(repoRoot, config) {
17
+ const outcomes = [];
18
+ const unknownCheckIds = [];
19
+ for (const [id, entry] of Object.entries(config.Checks)) {
20
+ const check = GetCheck(id);
21
+ if (!check) {
22
+ // Named but unregistered: a typo, or a standard removed from the package. Either way
23
+ // the repo believes it is enforcing something it is not, so say so out loud.
24
+ unknownCheckIds.push(id);
25
+ continue;
26
+ }
27
+ if (entry.Severity === 'off')
28
+ continue;
29
+ const roots = entry.Roots ?? config.Roots ?? check.DefaultRoots;
30
+ const options = { ...(check.DefaultOptions ?? {}), ...(entry.Options ?? {}) };
31
+ const result = await check.Run({ RepoRoot: repoRoot, Roots: roots, Options: options });
32
+ outcomes.push({
33
+ Check: check,
34
+ Severity: entry.Severity,
35
+ Violations: result.Violations,
36
+ Notes: result.Notes ?? [],
37
+ });
38
+ }
39
+ const available = STANDARD_CHECKS.filter((c) => !(c.Id in config.Checks)).map((Check) => ({
40
+ Check,
41
+ PostdatesAdoption: IsNewerThan(Check.Since, config.StandardsVersion),
42
+ }));
43
+ const errorCount = outcomes.filter((o) => o.Severity === 'error').reduce((n, o) => n + o.Violations.length, 0);
44
+ const warningCount = outcomes.filter((o) => o.Severity === 'warn').reduce((n, o) => n + o.Violations.length, 0);
45
+ return { Outcomes: outcomes, Available: available, UnknownCheckIds: unknownCheckIds, ErrorCount: errorCount, WarningCount: warningCount };
46
+ }
47
+ function formatViolation(v) {
48
+ const location = v.Line > 0 ? `${v.File}:${v.Line}` : v.File;
49
+ return ` ${location} ${v.Message}`;
50
+ }
51
+ /**
52
+ * Render a run for a terminal.
53
+ *
54
+ * Every failing check prints its docs URL. A rule whose reasoning is one search away gets followed;
55
+ * one that just says "no" gets worked around.
56
+ */
57
+ export function FormatSummary(summary, config) {
58
+ const lines = [];
59
+ for (const outcome of summary.Outcomes) {
60
+ const mark = outcome.Violations.length === 0 ? '✓' : outcome.Severity === 'error' ? '✗' : '!';
61
+ const label = outcome.Violations.length === 0 ? '' : ` — ${outcome.Violations.length} violation(s)`;
62
+ lines.push(`${mark} ${outcome.Check.Id} [${outcome.Severity}]${label}`);
63
+ for (const note of outcome.Notes)
64
+ lines.push(` ${note}`);
65
+ for (const violation of outcome.Violations)
66
+ lines.push(formatViolation(violation));
67
+ if (outcome.Violations.length > 0)
68
+ lines.push(` → ${outcome.Check.DocsUrl}`);
69
+ }
70
+ if (summary.UnknownCheckIds.length > 0) {
71
+ lines.push('');
72
+ lines.push(`! ${CONFIG_LABEL} names ${summary.UnknownCheckIds.length} check(s) this version does not know:`);
73
+ for (const id of summary.UnknownCheckIds)
74
+ lines.push(` ${id}`);
75
+ lines.push(' They are NOT being enforced. Fix the name, or remove the entry.');
76
+ }
77
+ const newer = summary.Available.filter((a) => a.PostdatesAdoption);
78
+ const older = summary.Available.filter((a) => !a.PostdatesAdoption);
79
+ if (newer.length > 0) {
80
+ lines.push('');
81
+ lines.push(`${newer.length} standard(s) added since this repo adopted ${config.StandardsVersion}:`);
82
+ for (const { Check } of newer)
83
+ lines.push(` ${Check.Id} (since ${Check.Since}) ${Check.Title}`);
84
+ lines.push(' They are NOT active. Review with `mj standards adopt --upgrade`.');
85
+ }
86
+ if (older.length > 0) {
87
+ lines.push('');
88
+ lines.push(`${older.length} standard(s) available but not adopted:`);
89
+ for (const { Check } of older)
90
+ lines.push(` ${Check.Id} ${Check.Title}`);
91
+ lines.push(' Enable with `mj standards adopt --check <id>`.');
92
+ }
93
+ lines.push('');
94
+ if (summary.ErrorCount > 0) {
95
+ lines.push(`✗ ${summary.ErrorCount} error(s), ${summary.WarningCount} warning(s).`);
96
+ }
97
+ else if (summary.WarningCount > 0) {
98
+ lines.push(`✓ No errors. ${summary.WarningCount} warning(s) — not failing the build.`);
99
+ }
100
+ else {
101
+ lines.push('✓ All adopted standards pass.');
102
+ }
103
+ return lines.join('\n');
104
+ }
105
+ const CONFIG_LABEL = '.mj-standards.json';
106
+ /** Exit code for a run: non-zero only for `error`-severity violations. */
107
+ export function ExitCodeFor(summary) {
108
+ return summary.ErrorCount > 0 ? 1 : 0;
109
+ }
110
+ //# sourceMappingURL=runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.js","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @fileoverview `adopt` — turn a repo that has never heard of MJ standards into one that enforces
3
+ * them, in a single command.
4
+ *
5
+ * The gap between "we published our standards" and "our standards are actually followed" is almost
6
+ * entirely setup friction. Every step below is something a team would otherwise have to read a
7
+ * guide to do, get slightly wrong, and then not notice for six months.
8
+ *
9
+ * Everything here is **idempotent and additive**. Re-running `adopt` on a configured repo reports
10
+ * what is already in place rather than overwriting it — in particular it never lowers a severity a
11
+ * repo has raised, and never rewrites a CI file a team has customized.
12
+ *
13
+ * @module @memberjunction/standards
14
+ */
15
+ import type { StandardsConfig } from './types.js';
16
+ /** One thing `adopt` did, or deliberately didn't. */
17
+ export interface AdoptAction {
18
+ Kind: 'created' | 'updated' | 'skipped';
19
+ What: string;
20
+ Detail?: string;
21
+ }
22
+ export interface AdoptOptions {
23
+ RepoRoot: string;
24
+ /** The version being adopted against — normally the installed MJ/CLI version. */
25
+ Version: string;
26
+ /** Limit to these check ids. Empty means "every check at or below `Version`". */
27
+ OnlyChecks?: string[];
28
+ /**
29
+ * Review and enable standards newer than the repo's recorded `StandardsVersion`, and bump it.
30
+ * Without this, `adopt` on a configured repo leaves the version line alone — which is what
31
+ * makes upgrading the package safe.
32
+ */
33
+ Upgrade?: boolean;
34
+ /** Report what would happen, change nothing. */
35
+ DryRun?: boolean;
36
+ /** Also write a CI workflow. */
37
+ Ci?: 'github' | 'none';
38
+ /** Add an `mj:standards` npm script to package.json. */
39
+ AddNpmScript?: boolean;
40
+ /**
41
+ * Declare `mjUILayer` on packages that already comply.
42
+ *
43
+ * Without this, a fresh adoption produces a config that enforces nothing — every package is
44
+ * undeclared, so every package is skipped, and the repo gets a green check that means nothing.
45
+ * Declaring the already-clean packages is what makes the first run honest.
46
+ */
47
+ DeclareCompliant?: boolean;
48
+ }
49
+ export interface AdoptResult {
50
+ Actions: AdoptAction[];
51
+ Config: StandardsConfig;
52
+ }
53
+ /** Adopt (or re-adopt) MJ standards in a repo. */
54
+ export declare function Adopt(options: AdoptOptions): AdoptResult;
55
+ //# sourceMappingURL=scaffold.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAOH,OAAO,KAAK,EAA8B,eAAe,EAAE,MAAM,YAAY,CAAC;AAG9E,qDAAqD;AACrD,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gDAAgD;IAChD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gCAAgC;IAChC,EAAE,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IACvB,wDAAwD;IACxD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IACxB,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,MAAM,EAAE,eAAe,CAAC;CAC3B;AAgED,kDAAkD;AAClD,wBAAgB,KAAK,CAAC,OAAO,EAAE,YAAY,GAAG,WAAW,CAiExD"}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * @fileoverview `adopt` — turn a repo that has never heard of MJ standards into one that enforces
3
+ * them, in a single command.
4
+ *
5
+ * The gap between "we published our standards" and "our standards are actually followed" is almost
6
+ * entirely setup friction. Every step below is something a team would otherwise have to read a
7
+ * guide to do, get slightly wrong, and then not notice for six months.
8
+ *
9
+ * Everything here is **idempotent and additive**. Re-running `adopt` on a configured repo reports
10
+ * what is already in place rather than overwriting it — in particular it never lowers a severity a
11
+ * repo has raised, and never rewrites a CI file a team has customized.
12
+ *
13
+ * @module @memberjunction/standards
14
+ */
15
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
16
+ import { dirname, join } from 'node:path';
17
+ import { LoadConfig, SaveConfig, HasConfig } from './config.js';
18
+ import { GetCheck, STANDARD_CHECKS } from './registry.js';
19
+ import { DeclareLayer, ProbeUndeclaredPackages } from './checks/ui-layers.js';
20
+ import { IsNewerThan } from './version.js';
21
+ /** Which checks a fresh adoption should enable: everything at or below the adopted version. */
22
+ function checksForVersion(version, only) {
23
+ const candidates = only?.length
24
+ ? only.map((id) => GetCheck(id)).filter((c) => !!c)
25
+ : STANDARD_CHECKS.filter((c) => !IsNewerThan(c.Since, version));
26
+ return [...candidates];
27
+ }
28
+ /**
29
+ * Enable a check in a config, without ever weakening what is already there.
30
+ *
31
+ * A repo that has raised `ui-layers` to `error` and turned on `requireDeclared` must not have that
32
+ * undone by someone re-running `adopt` — the command's job is to get repos started, not to reset
33
+ * them to the defaults.
34
+ */
35
+ function mergeCheck(existing, check) {
36
+ if (existing)
37
+ return { Entry: existing, Changed: false };
38
+ return {
39
+ Entry: {
40
+ Severity: check.DefaultSeverity,
41
+ Roots: check.DefaultRoots,
42
+ ...(check.DefaultOptions ? { Options: { ...check.DefaultOptions } } : {}),
43
+ },
44
+ Changed: true,
45
+ };
46
+ }
47
+ const GITHUB_WORKFLOW_PATH = '.github/workflows/mj-standards.yml';
48
+ const GITHUB_WORKFLOW = `name: MJ standards
49
+
50
+ # Generated by \`mj standards adopt\`. Safe to edit — this file is never overwritten once it exists.
51
+ #
52
+ # What runs is decided by .mj-standards.json, NOT by this file. Adding a standard to
53
+ # @memberjunction/standards does not change this workflow's result until someone runs
54
+ # \`mj standards adopt --upgrade\` and commits the config change.
55
+
56
+ on:
57
+ pull_request:
58
+ push:
59
+ branches: [next, main]
60
+
61
+ concurrency:
62
+ group: mj-standards-\${{ github.ref }}
63
+ cancel-in-progress: true
64
+
65
+ jobs:
66
+ standards:
67
+ name: MJ standards
68
+ runs-on: ubuntu-latest
69
+ timeout-minutes: 10
70
+ steps:
71
+ - uses: actions/checkout@v4
72
+ - uses: actions/setup-node@v4
73
+ with:
74
+ node-version: 22
75
+ cache: npm
76
+ - run: npm ci
77
+ - name: Check adopted standards
78
+ run: npx mj standards check
79
+ `;
80
+ /** Adopt (or re-adopt) MJ standards in a repo. */
81
+ export function Adopt(options) {
82
+ const actions = [];
83
+ const { RepoRoot, Version, DryRun } = options;
84
+ const existing = HasConfig(RepoRoot) ? LoadConfig(RepoRoot) : null;
85
+ const config = existing ?? { StandardsVersion: Version, Checks: {} };
86
+ // The version we select checks against. On a fresh adopt that is the installed version; on a
87
+ // re-run it stays at whatever the repo recorded, unless --upgrade says otherwise. This single
88
+ // line is what stops a package upgrade from activating new rules.
89
+ const selectionVersion = existing ? (options.Upgrade ? Version : existing.StandardsVersion) : Version;
90
+ for (const check of checksForVersion(selectionVersion, options.OnlyChecks)) {
91
+ const { Entry, Changed } = mergeCheck(config.Checks[check.Id], check);
92
+ config.Checks[check.Id] = Entry;
93
+ actions.push(Changed
94
+ ? { Kind: 'created', What: `check "${check.Id}"`, Detail: `severity ${Entry.Severity}, roots ${(Entry.Roots ?? []).join(', ')}` }
95
+ : { Kind: 'skipped', What: `check "${check.Id}"`, Detail: `already configured (severity ${Entry.Severity}) — left alone` });
96
+ }
97
+ if (options.Upgrade && existing && IsNewerThan(Version, existing.StandardsVersion)) {
98
+ config.StandardsVersion = Version;
99
+ actions.push({ Kind: 'updated', What: 'StandardsVersion', Detail: `${existing.StandardsVersion} → ${Version}` });
100
+ }
101
+ if (!DryRun)
102
+ SaveConfig(RepoRoot, config);
103
+ actions.push({ Kind: existing ? 'updated' : 'created', What: '.mj-standards.json' });
104
+ if (options.Ci === 'github') {
105
+ const path = join(RepoRoot, GITHUB_WORKFLOW_PATH);
106
+ if (existsSync(path)) {
107
+ actions.push({ Kind: 'skipped', What: GITHUB_WORKFLOW_PATH, Detail: 'already exists — not overwritten' });
108
+ }
109
+ else {
110
+ if (!DryRun) {
111
+ mkdirSync(dirname(path), { recursive: true });
112
+ writeFileSync(path, GITHUB_WORKFLOW, 'utf8');
113
+ }
114
+ actions.push({ Kind: 'created', What: GITHUB_WORKFLOW_PATH });
115
+ }
116
+ }
117
+ if (options.AddNpmScript) {
118
+ const path = join(RepoRoot, 'package.json');
119
+ if (!existsSync(path)) {
120
+ actions.push({ Kind: 'skipped', What: 'npm script', Detail: 'no package.json at the repo root' });
121
+ }
122
+ else {
123
+ const manifest = JSON.parse(readFileSync(path, 'utf8'));
124
+ manifest.scripts = manifest.scripts ?? {};
125
+ if (manifest.scripts['mj:standards']) {
126
+ actions.push({ Kind: 'skipped', What: 'npm script "mj:standards"', Detail: 'already defined — left alone' });
127
+ }
128
+ else {
129
+ manifest.scripts['mj:standards'] = 'mj standards check';
130
+ if (!DryRun)
131
+ writeFileSync(path, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
132
+ actions.push({ Kind: 'created', What: 'npm script "mj:standards"' });
133
+ }
134
+ }
135
+ }
136
+ if (options.DeclareCompliant) {
137
+ actions.push(...declareCompliantUILayers(config, RepoRoot, DryRun === true));
138
+ }
139
+ return { Actions: actions, Config: config };
140
+ }
141
+ /**
142
+ * Declare `mjUILayer` on undeclared packages that would pass.
143
+ *
144
+ * Takes the **strictest** layer each package qualifies for. `shell` is never auto-assigned: it
145
+ * checks nothing, so every package passes as `shell`, and assigning it would hand a permanent
146
+ * exemption to exactly the packages that need work. (An earlier version of this did assign it, and
147
+ * a two-package test repo caught it immediately — the deliberately non-compliant package came back
148
+ * declared `shell` and passing.)
149
+ *
150
+ * Packages that pass as nothing are reported, not declared. A declaration that immediately fails
151
+ * is a worse first impression than declaring fewer and saying plainly what is left.
152
+ */
153
+ function declareCompliantUILayers(config, repoRoot, dryRun) {
154
+ const actions = [];
155
+ const entry = config.Checks['ui-layers'];
156
+ const check = GetCheck('ui-layers');
157
+ if (!entry || entry.Severity === 'off' || !check)
158
+ return actions;
159
+ const roots = entry.Roots ?? config.Roots ?? check.DefaultRoots;
160
+ const undeclared = ProbeUndeclaredPackages(repoRoot, roots);
161
+ if (undeclared.length === 0)
162
+ return actions;
163
+ for (const pkg of undeclared) {
164
+ const strictest = pkg.WouldPassAs[0];
165
+ if (!strictest) {
166
+ actions.push({
167
+ Kind: 'skipped',
168
+ What: `mjUILayer for ${pkg.Name}`,
169
+ Detail: 'violations at every auto-assignable layer — fix them, then declare it',
170
+ });
171
+ continue;
172
+ }
173
+ if (!dryRun)
174
+ DeclareLayer(pkg.Dir, strictest);
175
+ actions.push({ Kind: 'created', What: `mjUILayer for ${pkg.Name}`, Detail: strictest });
176
+ }
177
+ return actions;
178
+ }
179
+ //# sourceMappingURL=scaffold.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * @fileoverview The type model for MJ standards.
3
+ *
4
+ * The whole design turns on one requirement: **a new standard must never break an older repo's
5
+ * build.** Everything below exists to make that structurally true rather than a promise.
6
+ *
7
+ * @module @memberjunction/standards
8
+ */
9
+ /**
10
+ * How loudly a check reports.
11
+ *
12
+ * - `off` — registered but not run. The state every check is in until a repo opts in.
13
+ * - `warn` — reported, exit code stays 0. What a newly-adopted check should be for one cycle.
14
+ * - `error` — reported, exit code 1.
15
+ */
16
+ export type Severity = 'off' | 'warn' | 'error';
17
+ /** One thing a check found wrong. */
18
+ export interface Violation {
19
+ /** Repo-relative path of the offending file. */
20
+ File: string;
21
+ /** 1-based line, or 0 for a whole-file / manifest finding. */
22
+ Line: number;
23
+ /** What is wrong, and — always — what to do instead. */
24
+ Message: string;
25
+ /** The package the file belongs to, when the check knows it. */
26
+ Package?: string;
27
+ }
28
+ /** Everything a check is given to do its work. */
29
+ export interface CheckContext {
30
+ /** Absolute path to the repository root. */
31
+ RepoRoot: string;
32
+ /** Roots to scan, repo-relative. Comes from the check's own config, falling back to the repo's. */
33
+ Roots: string[];
34
+ /** The check's own options block from `.mj-standards.json`, verbatim. */
35
+ Options: Readonly<Record<string, unknown>>;
36
+ }
37
+ /** What a check returns. */
38
+ export interface CheckResult {
39
+ Violations: Violation[];
40
+ /**
41
+ * Things the check wants said that are not violations — "12 packages scanned, 3 skipped".
42
+ * Printed under the check's heading; never affects exit code.
43
+ */
44
+ Notes?: string[];
45
+ }
46
+ /**
47
+ * A standard, as registered.
48
+ *
49
+ * ## The version contract
50
+ *
51
+ * `Since` is the MJ version that introduced the check. A repo records the version it adopted
52
+ * against (`standardsVersion` in its config). Checks introduced *after* that version are reported
53
+ * as **available**, never enabled — so upgrading `@memberjunction/standards` can add rules without
54
+ * changing any repo's result until a human runs `mj standards adopt --upgrade`.
55
+ *
56
+ * `DefaultSeverity` is what `adopt` writes into a repo's config when it first enables the check.
57
+ * It is a starting point for new adopters, **not** a live value: changing it here never changes
58
+ * an already-adopted repo, because the repo's config holds its own severity.
59
+ *
60
+ * That asymmetry is the whole point. Severity can decay forward (`warn` → `error` on a major, by
61
+ * the repo's own choice) and never backward into a repo that has already shipped.
62
+ */
63
+ export interface StandardCheck {
64
+ /** Stable, kebab-case identifier. Used as the config key — renaming one is a breaking change. */
65
+ Id: string;
66
+ /** One line, imperative: what the check enforces. */
67
+ Title: string;
68
+ /** MJ version that introduced this check, e.g. `'6.0.0'`. */
69
+ Since: string;
70
+ /** Severity `adopt` writes for a NEW adopter. Never applied to an already-configured repo. */
71
+ DefaultSeverity: Exclude<Severity, 'off'>;
72
+ /** Where the reasoning lives. Printed with every failure — a rule without a why gets ignored. */
73
+ DocsUrl: string;
74
+ /** Longer explanation, shown by `mj standards list`. */
75
+ Description: string;
76
+ /** Default roots to scan when the repo doesn't name any. */
77
+ DefaultRoots: string[];
78
+ /** Options `adopt` writes for a new adopter. */
79
+ DefaultOptions?: Record<string, unknown>;
80
+ /** Do the work. Must not throw for ordinary findings — return them as violations. */
81
+ Run(context: CheckContext): Promise<CheckResult> | CheckResult;
82
+ }
83
+ /** A single check's entry in `.mj-standards.json`. */
84
+ export interface CheckConfig {
85
+ Severity: Severity;
86
+ /** Overrides the check's `DefaultRoots`. */
87
+ Roots?: string[];
88
+ /** Check-specific options. */
89
+ Options?: Record<string, unknown>;
90
+ }
91
+ /**
92
+ * `.mj-standards.json` — a repo's adoption record.
93
+ *
94
+ * Deliberately explicit rather than convention-driven: reading this file should tell you exactly
95
+ * which standards this repo has agreed to and how strictly, without knowing anything about the
96
+ * version of the tool that will read it.
97
+ */
98
+ export interface StandardsConfig {
99
+ /** JSON-schema pointer, for editor completion. */
100
+ $schema?: string;
101
+ /**
102
+ * The MJ version this repo adopted standards against.
103
+ *
104
+ * Checks with a `Since` greater than this are reported as available and are NOT run. Bumping
105
+ * this is a deliberate act (`mj standards adopt --upgrade`), never a side effect of upgrading
106
+ * the package.
107
+ */
108
+ StandardsVersion: string;
109
+ /** Default roots for checks that don't name their own. */
110
+ Roots?: string[];
111
+ /** Per-check configuration. A check absent from this map does not run. */
112
+ Checks: Record<string, CheckConfig>;
113
+ }
114
+ /** One check's outcome, after severity is applied. */
115
+ export interface CheckOutcome {
116
+ Check: StandardCheck;
117
+ Severity: Exclude<Severity, 'off'>;
118
+ Violations: Violation[];
119
+ Notes: string[];
120
+ }
121
+ /** The whole run. */
122
+ export interface RunSummary {
123
+ Outcomes: CheckOutcome[];
124
+ /** Registered checks the repo has not adopted, and whether they postdate its adoption. */
125
+ Available: Array<{
126
+ Check: StandardCheck;
127
+ PostdatesAdoption: boolean;
128
+ }>;
129
+ /** Config keys that match no registered check — usually a typo or a removed standard. */
130
+ UnknownCheckIds: string[];
131
+ ErrorCount: number;
132
+ WarningCount: number;
133
+ }
134
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhD,qCAAqC;AACrC,MAAM,WAAW,SAAS;IACtB,gDAAgD;IAChD,IAAI,EAAE,MAAM,CAAC;IACb,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,kDAAkD;AAClD,MAAM,WAAW,YAAY;IACzB,4CAA4C;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,mGAAmG;IACnG,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,yEAAyE;IACzE,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC9C;AAED,4BAA4B;AAC5B,MAAM,WAAW,WAAW;IACxB,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,aAAa;IAC1B,iGAAiG;IACjG,EAAE,EAAE,MAAM,CAAC;IACX,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,eAAe,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAC1C,iGAAiG;IACjG,OAAO,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,gDAAgD;IAChD,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,qFAAqF;IACrF,GAAG,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAAC;CAClE;AAED,sDAAsD;AACtD,MAAM,WAAW,WAAW;IACxB,QAAQ,EAAE,QAAQ,CAAC;IACnB,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,8BAA8B;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC5B,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;OAMG;IACH,gBAAgB,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;CACvC;AAED,sDAAsD;AACtD,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IACnC,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,qBAAqB;AACrB,MAAM,WAAW,UAAU;IACvB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,0FAA0F;IAC1F,SAAS,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,aAAa,CAAC;QAAC,iBAAiB,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACvE,yFAAyF;IACzF,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACxB"}
package/dist/types.js ADDED
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @fileoverview The type model for MJ standards.
3
+ *
4
+ * The whole design turns on one requirement: **a new standard must never break an older repo's
5
+ * build.** Everything below exists to make that structurally true rather than a promise.
6
+ *
7
+ * @module @memberjunction/standards
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}