@memberjunction/standards 0.0.0 → 6.1.0-edge.1
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/LICENSE +7 -0
- package/README.md +120 -28
- package/bin/run.js +92 -0
- package/dist/checks/ui-layers.d.ts +78 -0
- package/dist/checks/ui-layers.d.ts.map +1 -0
- package/dist/checks/ui-layers.js +407 -0
- package/dist/checks/ui-layers.js.map +1 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +98 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +27 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +31 -0
- package/dist/registry.js.map +1 -0
- package/dist/runner.d.ts +25 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +110 -0
- package/dist/runner.js.map +1 -0
- package/dist/scaffold.d.ts +55 -0
- package/dist/scaffold.d.ts.map +1 -0
- package/dist/scaffold.js +179 -0
- package/dist/scaffold.js.map +1 -0
- package/dist/types.d.ts +134 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +37 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +47 -0
- package/dist/version.js.map +1 -0
- package/package.json +35 -7
- package/schema/mj-standards.schema.json +50 -0
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The UI layering standard.
|
|
3
|
+
*
|
|
4
|
+
* Enforces the boundaries in the MJ repo's `guides/UI_LAYERING_GUIDE.md`:
|
|
5
|
+
*
|
|
6
|
+
* runtime (L0) pure TS — no Angular at all
|
|
7
|
+
* widgets (L1+L2) framework-clean Angular — no Router, no Explorer, no global provider
|
|
8
|
+
* surface (L3) Explorer surfaces — NavigationService only, never Router
|
|
9
|
+
* shell the navigation layer ITSELF — the documented Router exception
|
|
10
|
+
*
|
|
11
|
+
* A package opts in by declaring `"mjUILayer"` in its own package.json. A package without the
|
|
12
|
+
* field is skipped, unless `requireDeclared` is on — see the option's docs for why that matters.
|
|
13
|
+
*
|
|
14
|
+
* @module @memberjunction/standards
|
|
15
|
+
*/
|
|
16
|
+
import { readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { join, relative } from 'node:path';
|
|
18
|
+
const VALID_LAYERS = ['runtime', 'widgets', 'surface', 'shell'];
|
|
19
|
+
/** Module specifiers a layer may never import, with the reason shown on failure. */
|
|
20
|
+
const FORBIDDEN_MODULES = {
|
|
21
|
+
runtime: [[/^@angular\//, 'L0 runtime code must be pure TypeScript — importable from Node, a worker, or a non-Angular host']],
|
|
22
|
+
widgets: [
|
|
23
|
+
[/^@angular\/router$/, 'widgets must not route — emit an event and let the host decide'],
|
|
24
|
+
[/^@memberjunction\/ng-shared$/, 'ng-shared is Explorer (NavigationService / BaseResourceComponent) — that is L3'],
|
|
25
|
+
[/^@memberjunction\/ng-explorer/, 'Explorer packages are L3 — a widget that imports one cannot be reused'],
|
|
26
|
+
],
|
|
27
|
+
// L3 lives inside an app that has Router configured, and `RouterModule` is a legitimate import
|
|
28
|
+
// for declarative `routerLink` chrome. What breaks the shell is *imperative* navigation, so the
|
|
29
|
+
// surface rule bans the symbols below rather than the module.
|
|
30
|
+
surface: [],
|
|
31
|
+
// The shell IS the navigation layer — what NavigationService is implemented on top of. Banning
|
|
32
|
+
// Router here would ban the implementation of the rule.
|
|
33
|
+
shell: [],
|
|
34
|
+
};
|
|
35
|
+
/** Imported symbol names a layer may never bind, whatever module they came from. */
|
|
36
|
+
const FORBIDDEN_SYMBOLS = {
|
|
37
|
+
runtime: [],
|
|
38
|
+
widgets: [
|
|
39
|
+
['Router', 'routing belongs to the host'],
|
|
40
|
+
['ActivatedRoute', 'routing belongs to the host'],
|
|
41
|
+
['NavigationEnd', 'routing belongs to the host'],
|
|
42
|
+
['RouterModule', 'routing belongs to the host'],
|
|
43
|
+
['NavigationService', 'navigation is L3 — emit an intent event instead'],
|
|
44
|
+
['SharedService', 'SharedService is Explorer chrome — emit a notification event instead'],
|
|
45
|
+
['BaseResourceComponent', 'a resource component IS an Explorer surface — that class belongs in the L3 package'],
|
|
46
|
+
['BaseDashboard', 'a dashboard IS an Explorer surface — that class belongs in the L3 package'],
|
|
47
|
+
],
|
|
48
|
+
surface: [
|
|
49
|
+
['Router', 'use NavigationService — Router desyncs the shell tab state from the URL'],
|
|
50
|
+
['ActivatedRoute', 'use GetQueryParams() / OnQueryParamsChanged()'],
|
|
51
|
+
['NavigationEnd', 'the shell owns URL sync — never subscribe to Router events'],
|
|
52
|
+
],
|
|
53
|
+
shell: [],
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* Source constructs a layer may never contain.
|
|
57
|
+
*
|
|
58
|
+
* Note the `\(\s*\)` — only the ZERO-ARGUMENT form is a violation. `new RunView(provider)` passes
|
|
59
|
+
* a provider explicitly and is correct; a blunter pattern flagged it, which is exactly the kind of
|
|
60
|
+
* false positive that gets a gate switched off.
|
|
61
|
+
*/
|
|
62
|
+
const FORBIDDEN_PATTERNS = {
|
|
63
|
+
runtime: [],
|
|
64
|
+
widgets: [
|
|
65
|
+
[/\bnew\s+RunViews?\s*\(\s*\)/, 'new RunView()', 'binds the global provider — use RunView.FromMetadataProvider(this.ProviderToUse)'],
|
|
66
|
+
[/\bnew\s+RunQuery\s*\(\s*\)/, 'new RunQuery()', 'binds the global provider — use this.RunQueryToUse'],
|
|
67
|
+
[/\bnew\s+Metadata\s*\(\s*\)/, 'new Metadata()', 'binds the global provider — use this.ProviderToUse'], // global-provider-ok: this is the pattern TABLE (a documentation string), not a call site
|
|
68
|
+
],
|
|
69
|
+
surface: [],
|
|
70
|
+
shell: [],
|
|
71
|
+
};
|
|
72
|
+
/** Dependency specifiers a layer may never declare. The manifest half of the module ban. */
|
|
73
|
+
const FORBIDDEN_DEPS = {
|
|
74
|
+
runtime: [/^@angular\//],
|
|
75
|
+
widgets: [/^@angular\/router$/, /^@memberjunction\/ng-shared$/, /^@memberjunction\/ng-explorer/],
|
|
76
|
+
surface: [],
|
|
77
|
+
shell: [],
|
|
78
|
+
};
|
|
79
|
+
const SOURCE_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts'];
|
|
80
|
+
const SKIP_DIRS = new Set(['node_modules', 'dist', 'build', '.git', '.angular', 'coverage', 'generated']);
|
|
81
|
+
const ALLOW_MARKER = 'mj-ui-layers-allow';
|
|
82
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
83
|
+
// Source analysis
|
|
84
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
85
|
+
/**
|
|
86
|
+
* Blank out `//` and block comments, preserving every newline so line numbers still line up.
|
|
87
|
+
*
|
|
88
|
+
* Necessary because MJ source documents itself heavily: a JSDoc block explaining "this calls
|
|
89
|
+
* `new RunView()` on the global provider" is a comment about a violation, not a violation, and a
|
|
90
|
+
* gate that cannot tell the difference gets switched off. String literals are left alone — the
|
|
91
|
+
* banned constructs do not plausibly appear in one.
|
|
92
|
+
*/
|
|
93
|
+
export function StripComments(source) {
|
|
94
|
+
let out = '';
|
|
95
|
+
let index = 0;
|
|
96
|
+
while (index < source.length) {
|
|
97
|
+
const two = source.slice(index, index + 2);
|
|
98
|
+
if (two === '//') {
|
|
99
|
+
const end = source.indexOf('\n', index);
|
|
100
|
+
const stop = end === -1 ? source.length : end;
|
|
101
|
+
out += ' '.repeat(stop - index);
|
|
102
|
+
index = stop;
|
|
103
|
+
}
|
|
104
|
+
else if (two === '/*') {
|
|
105
|
+
const end = source.indexOf('*/', index + 2);
|
|
106
|
+
const stop = end === -1 ? source.length : end + 2;
|
|
107
|
+
out += source.slice(index, stop).replace(/[^\n]/g, ' ');
|
|
108
|
+
index = stop;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
out += source[index];
|
|
112
|
+
index++;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Extract every import/export-from specifier with the names it binds and its 1-based line.
|
|
119
|
+
*
|
|
120
|
+
* Import-aware rather than grep-based: matching the bare word `Router` anywhere produces false
|
|
121
|
+
* positives on `RouterLikeThing`, on comments and on strings. What matters is whether the module
|
|
122
|
+
* *binds* the symbol.
|
|
123
|
+
*/
|
|
124
|
+
export function ParseImports(source) {
|
|
125
|
+
const results = [];
|
|
126
|
+
const re = /(?:^|\n)\s*(?:import|export)\s*(?:type\s+)?(?:([\s\S]*?)\s*from\s*)?['"]([^'"]+)['"]/g;
|
|
127
|
+
let match;
|
|
128
|
+
while ((match = re.exec(source)) !== null) {
|
|
129
|
+
const [full, clause = '', specifier] = match;
|
|
130
|
+
const line = source.slice(0, match.index + full.indexOf(specifier)).split('\n').length;
|
|
131
|
+
const names = [];
|
|
132
|
+
const braced = /\{([\s\S]*?)\}/.exec(clause);
|
|
133
|
+
if (braced) {
|
|
134
|
+
for (const raw of braced[1].split(',')) {
|
|
135
|
+
const name = raw.trim().replace(/^type\s+/, '').split(/\s+as\s+/)[0].trim();
|
|
136
|
+
if (name)
|
|
137
|
+
names.push(name);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const defaultOrNamespace = clause.replace(/\{[\s\S]*?\}/, '').replace(/^type\s+/, '').trim().replace(/,$/, '').trim();
|
|
141
|
+
if (defaultOrNamespace && !defaultOrNamespace.startsWith('*'))
|
|
142
|
+
names.push(defaultOrNamespace);
|
|
143
|
+
results.push({ Specifier: specifier, Names: names, Line: line });
|
|
144
|
+
}
|
|
145
|
+
return results;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Does the given 1-based line carry a reviewed-exception marker, on itself or the line above?
|
|
149
|
+
*
|
|
150
|
+
* The preceding line counts because a real exception deserves a sentence of explanation and a
|
|
151
|
+
* trailing comment long enough to hold one is unreadable. Only ONE line above — a wider window
|
|
152
|
+
* would let a marker drift away from the thing it excuses.
|
|
153
|
+
*/
|
|
154
|
+
export function IsAllowed(lines, lineNumber) {
|
|
155
|
+
const own = lines[lineNumber - 1] ?? '';
|
|
156
|
+
const above = lineNumber >= 2 ? (lines[lineNumber - 2] ?? '') : '';
|
|
157
|
+
return own.includes(ALLOW_MARKER) || above.includes(ALLOW_MARKER);
|
|
158
|
+
}
|
|
159
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
160
|
+
// Filesystem walk
|
|
161
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
162
|
+
function findPackageDirs(root) {
|
|
163
|
+
const found = [];
|
|
164
|
+
const walk = (dir) => {
|
|
165
|
+
let entries;
|
|
166
|
+
try {
|
|
167
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (entries.some((e) => e.isFile() && e.name === 'package.json'))
|
|
173
|
+
found.push(dir);
|
|
174
|
+
for (const entry of entries) {
|
|
175
|
+
if (!entry.isDirectory() || SKIP_DIRS.has(entry.name))
|
|
176
|
+
continue;
|
|
177
|
+
walk(join(dir, entry.name));
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
walk(root);
|
|
181
|
+
return found;
|
|
182
|
+
}
|
|
183
|
+
function findSourceFiles(packageDir) {
|
|
184
|
+
const found = [];
|
|
185
|
+
const walk = (dir) => {
|
|
186
|
+
let entries;
|
|
187
|
+
try {
|
|
188
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
for (const entry of entries) {
|
|
194
|
+
const full = join(dir, entry.name);
|
|
195
|
+
if (entry.isDirectory()) {
|
|
196
|
+
if (!SKIP_DIRS.has(entry.name))
|
|
197
|
+
walk(full);
|
|
198
|
+
}
|
|
199
|
+
else if (SOURCE_EXTENSIONS.some((ext) => entry.name.endsWith(ext))) {
|
|
200
|
+
found.push(full);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
walk(packageDir);
|
|
205
|
+
return found;
|
|
206
|
+
}
|
|
207
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
208
|
+
// The check
|
|
209
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
210
|
+
/** Check one opted-in package. */
|
|
211
|
+
function checkPackage(packageDir, layer, repoRoot, packageName) {
|
|
212
|
+
const violations = [];
|
|
213
|
+
const add = (file, line, message) => {
|
|
214
|
+
violations.push({ File: relative(repoRoot, file), Line: line, Message: message, Package: packageName });
|
|
215
|
+
};
|
|
216
|
+
// Sources first: the manifest half needs to know which forbidden imports carry a reviewed
|
|
217
|
+
// exception, because the dependency-check gate (Knip) requires every real import to be
|
|
218
|
+
// declared — an excused import therefore FORCES a declaration, and flagging that declaration
|
|
219
|
+
// would put the two gates in deadlock.
|
|
220
|
+
const excusedSpecifiers = new Set();
|
|
221
|
+
for (const file of findSourceFiles(packageDir)) {
|
|
222
|
+
const raw = readFileSync(file, 'utf8');
|
|
223
|
+
const source = StripComments(raw);
|
|
224
|
+
// Allow-markers live IN comments, so they are read from the original text.
|
|
225
|
+
const lines = raw.split('\n');
|
|
226
|
+
const codeLines = source.split('\n');
|
|
227
|
+
for (const { Specifier, Names, Line } of ParseImports(source)) {
|
|
228
|
+
if (IsAllowed(lines, Line)) {
|
|
229
|
+
if (FORBIDDEN_MODULES[layer].some(([pattern]) => pattern.test(Specifier)))
|
|
230
|
+
excusedSpecifiers.add(Specifier);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
for (const [pattern, reason] of FORBIDDEN_MODULES[layer]) {
|
|
234
|
+
if (pattern.test(Specifier))
|
|
235
|
+
add(file, Line, `imports "${Specifier}" — ${reason}`);
|
|
236
|
+
}
|
|
237
|
+
for (const [symbol, reason] of FORBIDDEN_SYMBOLS[layer]) {
|
|
238
|
+
if (Names.includes(symbol))
|
|
239
|
+
add(file, Line, `imports "${symbol}" — ${reason}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
for (const [pattern, label, reason] of FORBIDDEN_PATTERNS[layer]) {
|
|
243
|
+
codeLines.forEach((text, index) => {
|
|
244
|
+
if (pattern.test(text) && !IsAllowed(lines, index + 1))
|
|
245
|
+
add(file, index + 1, `uses ${label} — ${reason}`);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const manifestPath = join(packageDir, 'package.json');
|
|
250
|
+
const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
|
|
251
|
+
const declared = { ...(manifest.dependencies ?? {}), ...(manifest.peerDependencies ?? {}) };
|
|
252
|
+
for (const dep of Object.keys(declared)) {
|
|
253
|
+
for (const pattern of FORBIDDEN_DEPS[layer]) {
|
|
254
|
+
if (!pattern.test(dep))
|
|
255
|
+
continue;
|
|
256
|
+
// A declaration backing a marker-excused import inherits the exception; an unexcused
|
|
257
|
+
// import of the same module still flags on its own line, so nothing slips through.
|
|
258
|
+
const backsExcusedImport = [...excusedSpecifiers].some((s) => s === dep || s.startsWith(`${dep}/`));
|
|
259
|
+
if (!backsExcusedImport)
|
|
260
|
+
add(manifestPath, 0, `declares "${dep}" — a "${layer}" package must not depend on it`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return violations;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Read a package's declared layer.
|
|
267
|
+
*
|
|
268
|
+
* Returns `undefined` for no declaration and `null` for an invalid one — different outcomes: the
|
|
269
|
+
* first is "not adopted", the second is a typo that must be reported.
|
|
270
|
+
*/
|
|
271
|
+
function readLayer(packageDir) {
|
|
272
|
+
try {
|
|
273
|
+
const manifest = JSON.parse(readFileSync(join(packageDir, 'package.json'), 'utf8'));
|
|
274
|
+
const raw = manifest.mjUILayer;
|
|
275
|
+
const layer = raw === undefined ? undefined : VALID_LAYERS.includes(raw) ? raw : null;
|
|
276
|
+
return { Name: manifest.name ?? packageDir, Layer: layer };
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
/** The registered UI layering standard. */
|
|
283
|
+
export const UILayersCheck = {
|
|
284
|
+
Id: 'ui-layers',
|
|
285
|
+
Title: 'Widgets must not import Router or MJ Explorer, and must read through ProviderToUse',
|
|
286
|
+
Since: '6.0.0',
|
|
287
|
+
DefaultSeverity: 'error',
|
|
288
|
+
DocsUrl: 'https://github.com/MemberJunction/MJ/blob/next/guides/UI_LAYERING_GUIDE.md',
|
|
289
|
+
Description: 'Four UI layers: L0 pure-TS runtime, L1 presentational widget, L2 composite widget, L3 Explorer surface. ' +
|
|
290
|
+
'Nothing below L3 may import @angular/router or an Explorer package; nothing below L3 may construct a ' +
|
|
291
|
+
'global-provider RunView/Metadata. Packages opt in with "mjUILayer" in their own package.json.',
|
|
292
|
+
DefaultRoots: ['packages'],
|
|
293
|
+
DefaultOptions: {
|
|
294
|
+
/**
|
|
295
|
+
* Turn "no mjUILayer field" from a skip into a violation, across every scanned root.
|
|
296
|
+
*
|
|
297
|
+
* Once a tree is clean, LOCK it. The way drift returns is not an edit to an existing
|
|
298
|
+
* package — it is a NEW package that never opts in, and from the outside "undeclared" is
|
|
299
|
+
* indistinguishable from "compliant".
|
|
300
|
+
*/
|
|
301
|
+
requireDeclared: false,
|
|
302
|
+
/**
|
|
303
|
+
* Lock only these subtrees (repo-relative path prefixes), leaving the rest adopting.
|
|
304
|
+
*
|
|
305
|
+
* This is the shape a real migration actually takes: one tree gets cleaned and must stay
|
|
306
|
+
* clean, while the others are still being worked through. Without it a repo has to choose
|
|
307
|
+
* between locking everything (impossible mid-migration) and locking nothing (which is how
|
|
308
|
+
* drift returns), and most will pick nothing.
|
|
309
|
+
*/
|
|
310
|
+
requireDeclaredIn: [],
|
|
311
|
+
},
|
|
312
|
+
Run(context) {
|
|
313
|
+
const violations = [];
|
|
314
|
+
const notes = [];
|
|
315
|
+
const undeclared = [];
|
|
316
|
+
let checkedCount = 0;
|
|
317
|
+
const requireDeclaredAll = context.Options['requireDeclared'] === true;
|
|
318
|
+
const lockedPrefixes = Array.isArray(context.Options['requireDeclaredIn'])
|
|
319
|
+
? context.Options['requireDeclaredIn'].filter((p) => typeof p === 'string')
|
|
320
|
+
: [];
|
|
321
|
+
const isLocked = (packageDir) => {
|
|
322
|
+
if (requireDeclaredAll)
|
|
323
|
+
return true;
|
|
324
|
+
const rel = relative(context.RepoRoot, packageDir).split('\\').join('/');
|
|
325
|
+
return lockedPrefixes.some((prefix) => rel === prefix || rel.startsWith(`${prefix}/`));
|
|
326
|
+
};
|
|
327
|
+
for (const root of context.Roots) {
|
|
328
|
+
const absolute = join(context.RepoRoot, root);
|
|
329
|
+
for (const packageDir of findPackageDirs(absolute)) {
|
|
330
|
+
const info = readLayer(packageDir);
|
|
331
|
+
if (!info)
|
|
332
|
+
continue;
|
|
333
|
+
if (info.Layer === undefined) {
|
|
334
|
+
undeclared.push({ Name: info.Name, Locked: isLocked(packageDir) });
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
if (info.Layer === null) {
|
|
338
|
+
violations.push({
|
|
339
|
+
File: relative(context.RepoRoot, join(packageDir, 'package.json')),
|
|
340
|
+
Line: 0,
|
|
341
|
+
Message: `mjUILayer is not one of ${VALID_LAYERS.join(' | ')}`,
|
|
342
|
+
Package: info.Name,
|
|
343
|
+
});
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
checkedCount++;
|
|
347
|
+
violations.push(...checkPackage(packageDir, info.Layer, context.RepoRoot, info.Name));
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
const lockedUndeclared = undeclared.filter((u) => u.Locked).map((u) => u.Name).sort();
|
|
351
|
+
for (const name of lockedUndeclared) {
|
|
352
|
+
violations.push({
|
|
353
|
+
File: 'package.json',
|
|
354
|
+
Line: 0,
|
|
355
|
+
Message: `${name} has no "mjUILayer" declaration and sits in a locked tree. ` +
|
|
356
|
+
`Add "mjUILayer": "${VALID_LAYERS.join('" | "')}" to its package.json.`,
|
|
357
|
+
Package: name,
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
const skipped = undeclared.length - lockedUndeclared.length;
|
|
361
|
+
notes.push(`${checkedCount} package(s) checked; ${lockedUndeclared.length} undeclared in a locked tree; ` +
|
|
362
|
+
`${skipped} undeclared elsewhere and skipped`);
|
|
363
|
+
return { Violations: violations, Notes: notes };
|
|
364
|
+
},
|
|
365
|
+
};
|
|
366
|
+
/**
|
|
367
|
+
* Layers a tool may assign automatically, strictest first.
|
|
368
|
+
*
|
|
369
|
+
* **`shell` is deliberately absent.** It checks nothing, so every package "passes" as `shell` —
|
|
370
|
+
* auto-assigning it would hand a permanent exemption to exactly the packages that most need work,
|
|
371
|
+
* and the repo would get a green check that means nothing. `shell` is a human decision, reserved
|
|
372
|
+
* for the handful of packages that genuinely implement navigation.
|
|
373
|
+
*/
|
|
374
|
+
const AUTO_ASSIGNABLE_LAYERS = ['runtime', 'widgets', 'surface'];
|
|
375
|
+
/**
|
|
376
|
+
* Find packages with no `mjUILayer` and work out which layers they could honestly claim.
|
|
377
|
+
*
|
|
378
|
+
* This is what lets `mj standards adopt` declare the already-compliant packages instead of leaving
|
|
379
|
+
* a repo with a config that enforces nothing. It answers the question by **running the rules**
|
|
380
|
+
* against a hypothetical layer, not by editing anything — probing by writing a field to
|
|
381
|
+
* package.json and reverting it would be a data-loss bug waiting for an interrupted process.
|
|
382
|
+
*
|
|
383
|
+
* `WouldPassAs` is ordered strictest-first and never contains `shell`, so a caller taking
|
|
384
|
+
* `WouldPassAs[0]` gets the most informative honest label, and a package that passes nothing comes
|
|
385
|
+
* back with an empty array — "needs work", not "call it shell".
|
|
386
|
+
*/
|
|
387
|
+
export function ProbeUndeclaredPackages(repoRoot, roots) {
|
|
388
|
+
const results = [];
|
|
389
|
+
for (const root of roots) {
|
|
390
|
+
for (const packageDir of findPackageDirs(join(repoRoot, root))) {
|
|
391
|
+
const info = readLayer(packageDir);
|
|
392
|
+
if (!info || info.Layer !== undefined)
|
|
393
|
+
continue;
|
|
394
|
+
const wouldPassAs = AUTO_ASSIGNABLE_LAYERS.filter((layer) => checkPackage(packageDir, layer, repoRoot, info.Name).length === 0);
|
|
395
|
+
results.push({ Dir: packageDir, Name: info.Name, WouldPassAs: wouldPassAs });
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return results;
|
|
399
|
+
}
|
|
400
|
+
/** Write `mjUILayer` into a package's manifest, preserving formatting as best as JSON allows. */
|
|
401
|
+
export function DeclareLayer(packageDir, layer) {
|
|
402
|
+
const path = join(packageDir, 'package.json');
|
|
403
|
+
const manifest = JSON.parse(readFileSync(path, 'utf8'));
|
|
404
|
+
manifest['mjUILayer'] = layer;
|
|
405
|
+
writeFileSync(path, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
406
|
+
}
|
|
407
|
+
//# sourceMappingURL=ui-layers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-layers.js","sourceRoot":"","sources":["../../src/checks/ui-layers.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Reading and writing `.mj-standards.json`.
|
|
3
|
+
*
|
|
4
|
+
* @module @memberjunction/standards
|
|
5
|
+
*/
|
|
6
|
+
import type { StandardsConfig } from './types.js';
|
|
7
|
+
/** The file a repo's adoption lives in, at the repository root. */
|
|
8
|
+
export declare const CONFIG_FILENAME = ".mj-standards.json";
|
|
9
|
+
/** Thrown when a config file exists but cannot be trusted. */
|
|
10
|
+
export declare class StandardsConfigError extends Error {
|
|
11
|
+
}
|
|
12
|
+
/** Absolute path to a repo's config file. */
|
|
13
|
+
export declare function ConfigPath(repoRoot: string): string;
|
|
14
|
+
/** Does this repo have an adoption record? */
|
|
15
|
+
export declare function HasConfig(repoRoot: string): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Load and validate a repo's config.
|
|
18
|
+
*
|
|
19
|
+
* Validation is strict and the errors name the fix, because the failure mode of a lenient loader
|
|
20
|
+
* is a config that looks adopted and enforces nothing. A typo in a severity should stop the build,
|
|
21
|
+
* not quietly disable a standard.
|
|
22
|
+
*/
|
|
23
|
+
export declare function LoadConfig(repoRoot: string): StandardsConfig;
|
|
24
|
+
/** Write a config, formatted for humans — this file gets read and edited by hand. */
|
|
25
|
+
export declare function SaveConfig(repoRoot: string, config: StandardsConfig): void;
|
|
26
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAyB,eAAe,EAAE,MAAM,YAAY,CAAC;AAEzE,mEAAmE;AACnE,eAAO,MAAM,eAAe,uBAAuB,CAAC;AAIpD,8DAA8D;AAC9D,qBAAa,oBAAqB,SAAQ,KAAK;CAAG;AAElD,6CAA6C;AAC7C,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,8CAA8C;AAC9C,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAsE5D;AAED,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAQ1E"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Reading and writing `.mj-standards.json`.
|
|
3
|
+
*
|
|
4
|
+
* @module @memberjunction/standards
|
|
5
|
+
*/
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
/** The file a repo's adoption lives in, at the repository root. */
|
|
9
|
+
export const CONFIG_FILENAME = '.mj-standards.json';
|
|
10
|
+
const VALID_SEVERITIES = ['off', 'warn', 'error'];
|
|
11
|
+
/** Thrown when a config file exists but cannot be trusted. */
|
|
12
|
+
export class StandardsConfigError extends Error {
|
|
13
|
+
}
|
|
14
|
+
/** Absolute path to a repo's config file. */
|
|
15
|
+
export function ConfigPath(repoRoot) {
|
|
16
|
+
return join(repoRoot, CONFIG_FILENAME);
|
|
17
|
+
}
|
|
18
|
+
/** Does this repo have an adoption record? */
|
|
19
|
+
export function HasConfig(repoRoot) {
|
|
20
|
+
return existsSync(ConfigPath(repoRoot));
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Load and validate a repo's config.
|
|
24
|
+
*
|
|
25
|
+
* Validation is strict and the errors name the fix, because the failure mode of a lenient loader
|
|
26
|
+
* is a config that looks adopted and enforces nothing. A typo in a severity should stop the build,
|
|
27
|
+
* not quietly disable a standard.
|
|
28
|
+
*/
|
|
29
|
+
export function LoadConfig(repoRoot) {
|
|
30
|
+
const path = ConfigPath(repoRoot);
|
|
31
|
+
if (!existsSync(path)) {
|
|
32
|
+
throw new StandardsConfigError(`No ${CONFIG_FILENAME} at ${repoRoot}. Run \`mj standards adopt\` to create one.`);
|
|
33
|
+
}
|
|
34
|
+
let parsed;
|
|
35
|
+
try {
|
|
36
|
+
parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
throw new StandardsConfigError(`${CONFIG_FILENAME} is not valid JSON: ${e instanceof Error ? e.message : String(e)}`);
|
|
40
|
+
}
|
|
41
|
+
if (typeof parsed !== 'object' || parsed === null) {
|
|
42
|
+
throw new StandardsConfigError(`${CONFIG_FILENAME} must contain a JSON object.`);
|
|
43
|
+
}
|
|
44
|
+
const raw = parsed;
|
|
45
|
+
const standardsVersion = raw['StandardsVersion'];
|
|
46
|
+
if (typeof standardsVersion !== 'string' || standardsVersion.length === 0) {
|
|
47
|
+
throw new StandardsConfigError(`${CONFIG_FILENAME} is missing "StandardsVersion". It records the MJ version this repo adopted ` +
|
|
48
|
+
`standards against, and is what stops newer standards from activating themselves here.`);
|
|
49
|
+
}
|
|
50
|
+
const checksRaw = raw['Checks'];
|
|
51
|
+
if (typeof checksRaw !== 'object' || checksRaw === null) {
|
|
52
|
+
throw new StandardsConfigError(`${CONFIG_FILENAME} is missing a "Checks" object.`);
|
|
53
|
+
}
|
|
54
|
+
const checks = {};
|
|
55
|
+
for (const [id, value] of Object.entries(checksRaw)) {
|
|
56
|
+
if (typeof value !== 'object' || value === null) {
|
|
57
|
+
throw new StandardsConfigError(`${CONFIG_FILENAME}: Checks["${id}"] must be an object.`);
|
|
58
|
+
}
|
|
59
|
+
const entry = value;
|
|
60
|
+
const severity = entry['Severity'];
|
|
61
|
+
if (typeof severity !== 'string' || !VALID_SEVERITIES.includes(severity)) {
|
|
62
|
+
throw new StandardsConfigError(`${CONFIG_FILENAME}: Checks["${id}"].Severity must be one of ${VALID_SEVERITIES.join(' | ')}.`);
|
|
63
|
+
}
|
|
64
|
+
const roots = entry['Roots'];
|
|
65
|
+
if (roots !== undefined && (!Array.isArray(roots) || roots.some((r) => typeof r !== 'string'))) {
|
|
66
|
+
throw new StandardsConfigError(`${CONFIG_FILENAME}: Checks["${id}"].Roots must be an array of strings.`);
|
|
67
|
+
}
|
|
68
|
+
const options = entry['Options'];
|
|
69
|
+
if (options !== undefined && (typeof options !== 'object' || options === null)) {
|
|
70
|
+
throw new StandardsConfigError(`${CONFIG_FILENAME}: Checks["${id}"].Options must be an object.`);
|
|
71
|
+
}
|
|
72
|
+
checks[id] = {
|
|
73
|
+
Severity: severity,
|
|
74
|
+
...(roots ? { Roots: roots } : {}),
|
|
75
|
+
...(options ? { Options: options } : {}),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const rootsRaw = raw['Roots'];
|
|
79
|
+
if (rootsRaw !== undefined && (!Array.isArray(rootsRaw) || rootsRaw.some((r) => typeof r !== 'string'))) {
|
|
80
|
+
throw new StandardsConfigError(`${CONFIG_FILENAME}: "Roots" must be an array of strings.`);
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
StandardsVersion: standardsVersion,
|
|
84
|
+
...(rootsRaw ? { Roots: rootsRaw } : {}),
|
|
85
|
+
Checks: checks,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
/** Write a config, formatted for humans — this file gets read and edited by hand. */
|
|
89
|
+
export function SaveConfig(repoRoot, config) {
|
|
90
|
+
const ordered = {
|
|
91
|
+
$schema: config.$schema ?? './node_modules/@memberjunction/standards/schema/mj-standards.schema.json',
|
|
92
|
+
StandardsVersion: config.StandardsVersion,
|
|
93
|
+
...(config.Roots ? { Roots: config.Roots } : {}),
|
|
94
|
+
Checks: config.Checks,
|
|
95
|
+
};
|
|
96
|
+
writeFileSync(ConfigPath(repoRoot), `${JSON.stringify(ordered, null, 2)}\n`, 'utf8');
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@memberjunction/standards` — MemberJunction's engineering standards, as runnable checks.
|
|
3
|
+
*
|
|
4
|
+
* ## What this is for
|
|
5
|
+
*
|
|
6
|
+
* MJ's standards live in two forms. **Judgment standards** are prose that has to be read — the
|
|
7
|
+
* guides in the MJ repo. **Executable standards** are the ones a machine can settle, and those are
|
|
8
|
+
* here. Shipping them as a versioned package rather than a file to copy is what lets a dozen repos
|
|
9
|
+
* — first-party, client, and external — stay aligned as the standards evolve.
|
|
10
|
+
*
|
|
11
|
+
* ## The property that makes it safe
|
|
12
|
+
*
|
|
13
|
+
* **A new standard never changes an existing repo's result.** A check is registered here as
|
|
14
|
+
* *available*; it does not run until a repo's `.mj-standards.json` names it. Checks whose `Since`
|
|
15
|
+
* postdates the repo's recorded `StandardsVersion` are reported as available and stay inert until
|
|
16
|
+
* a human runs `mj standards adopt --upgrade`.
|
|
17
|
+
*
|
|
18
|
+
* That means this package can ship new standards continuously, and a repo pinned on an older MJ
|
|
19
|
+
* never wakes up to a red build it did not ask for.
|
|
20
|
+
*
|
|
21
|
+
* ## Usage
|
|
22
|
+
*
|
|
23
|
+
* ```bash
|
|
24
|
+
* mj standards adopt --ci github --declare-compliant # scaffold a repo
|
|
25
|
+
* mj standards check # run what the repo adopted
|
|
26
|
+
* mj standards list # what exists, and what this repo uses
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @module @memberjunction/standards
|
|
30
|
+
*/
|
|
31
|
+
export * from './types.js';
|
|
32
|
+
export * from './version.js';
|
|
33
|
+
export * from './config.js';
|
|
34
|
+
export * from './registry.js';
|
|
35
|
+
export * from './runner.js';
|
|
36
|
+
export * from './scaffold.js';
|
|
37
|
+
export * from './checks/ui-layers.js';
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@memberjunction/standards` — MemberJunction's engineering standards, as runnable checks.
|
|
3
|
+
*
|
|
4
|
+
* ## What this is for
|
|
5
|
+
*
|
|
6
|
+
* MJ's standards live in two forms. **Judgment standards** are prose that has to be read — the
|
|
7
|
+
* guides in the MJ repo. **Executable standards** are the ones a machine can settle, and those are
|
|
8
|
+
* here. Shipping them as a versioned package rather than a file to copy is what lets a dozen repos
|
|
9
|
+
* — first-party, client, and external — stay aligned as the standards evolve.
|
|
10
|
+
*
|
|
11
|
+
* ## The property that makes it safe
|
|
12
|
+
*
|
|
13
|
+
* **A new standard never changes an existing repo's result.** A check is registered here as
|
|
14
|
+
* *available*; it does not run until a repo's `.mj-standards.json` names it. Checks whose `Since`
|
|
15
|
+
* postdates the repo's recorded `StandardsVersion` are reported as available and stay inert until
|
|
16
|
+
* a human runs `mj standards adopt --upgrade`.
|
|
17
|
+
*
|
|
18
|
+
* That means this package can ship new standards continuously, and a repo pinned on an older MJ
|
|
19
|
+
* never wakes up to a red build it did not ask for.
|
|
20
|
+
*
|
|
21
|
+
* ## Usage
|
|
22
|
+
*
|
|
23
|
+
* ```bash
|
|
24
|
+
* mj standards adopt --ci github --declare-compliant # scaffold a repo
|
|
25
|
+
* mj standards check # run what the repo adopted
|
|
26
|
+
* mj standards list # what exists, and what this repo uses
|
|
27
|
+
* ```
|
|
28
|
+
*
|
|
29
|
+
* @module @memberjunction/standards
|
|
30
|
+
*/
|
|
31
|
+
export * from './types.js';
|
|
32
|
+
export * from './version.js';
|
|
33
|
+
export * from './config.js';
|
|
34
|
+
export * from './registry.js';
|
|
35
|
+
export * from './runner.js';
|
|
36
|
+
export * from './scaffold.js';
|
|
37
|
+
export * from './checks/ui-layers.js';
|
|
38
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,uBAAuB,CAAC"}
|