@ploaness/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +13 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +161 -0
- package/dist/bin.js.map +1 -0
- package/dist/checks/assets.d.ts +30 -0
- package/dist/checks/assets.d.ts.map +1 -0
- package/dist/checks/assets.js +169 -0
- package/dist/checks/assets.js.map +1 -0
- package/dist/checks/containers.d.ts +9 -0
- package/dist/checks/containers.d.ts.map +1 -0
- package/dist/checks/containers.js +194 -0
- package/dist/checks/containers.js.map +1 -0
- package/dist/checks/conventions.d.ts +8 -0
- package/dist/checks/conventions.d.ts.map +1 -0
- package/dist/checks/conventions.js +77 -0
- package/dist/checks/conventions.js.map +1 -0
- package/dist/checks/dependencies.d.ts +11 -0
- package/dist/checks/dependencies.d.ts.map +1 -0
- package/dist/checks/dependencies.js +389 -0
- package/dist/checks/dependencies.js.map +1 -0
- package/dist/checks/editorconfig.d.ts +5 -0
- package/dist/checks/editorconfig.d.ts.map +1 -0
- package/dist/checks/editorconfig.js +38 -0
- package/dist/checks/editorconfig.js.map +1 -0
- package/dist/checks/generated.d.ts +10 -0
- package/dist/checks/generated.d.ts.map +1 -0
- package/dist/checks/generated.js +65 -0
- package/dist/checks/generated.js.map +1 -0
- package/dist/checks/history.d.ts +19 -0
- package/dist/checks/history.d.ts.map +1 -0
- package/dist/checks/history.js +71 -0
- package/dist/checks/history.js.map +1 -0
- package/dist/checks/images.d.ts +14 -0
- package/dist/checks/images.d.ts.map +1 -0
- package/dist/checks/images.js +100 -0
- package/dist/checks/images.js.map +1 -0
- package/dist/checks/install.d.ts +5 -0
- package/dist/checks/install.d.ts.map +1 -0
- package/dist/checks/install.js +21 -0
- package/dist/checks/install.js.map +1 -0
- package/dist/checks/integrity.d.ts +11 -0
- package/dist/checks/integrity.d.ts.map +1 -0
- package/dist/checks/integrity.js +66 -0
- package/dist/checks/integrity.js.map +1 -0
- package/dist/checks/payload.d.ts +7 -0
- package/dist/checks/payload.d.ts.map +1 -0
- package/dist/checks/payload.js +81 -0
- package/dist/checks/payload.js.map +1 -0
- package/dist/checks/preflight.d.ts +5 -0
- package/dist/checks/preflight.d.ts.map +1 -0
- package/dist/checks/preflight.js +48 -0
- package/dist/checks/preflight.js.map +1 -0
- package/dist/checks/references.d.ts +18 -0
- package/dist/checks/references.d.ts.map +1 -0
- package/dist/checks/references.js +100 -0
- package/dist/checks/references.js.map +1 -0
- package/dist/checks/suppressions.d.ts +5 -0
- package/dist/checks/suppressions.d.ts.map +1 -0
- package/dist/checks/suppressions.js +50 -0
- package/dist/checks/suppressions.js.map +1 -0
- package/dist/checks/tailwind.d.ts +5 -0
- package/dist/checks/tailwind.d.ts.map +1 -0
- package/dist/checks/tailwind.js +29 -0
- package/dist/checks/tailwind.js.map +1 -0
- package/dist/checks/tests.d.ts +8 -0
- package/dist/checks/tests.d.ts.map +1 -0
- package/dist/checks/tests.js +123 -0
- package/dist/checks/tests.js.map +1 -0
- package/dist/checks/toolchain.d.ts +25 -0
- package/dist/checks/toolchain.d.ts.map +1 -0
- package/dist/checks/toolchain.js +165 -0
- package/dist/checks/toolchain.js.map +1 -0
- package/dist/checks/tree.d.ts +6 -0
- package/dist/checks/tree.d.ts.map +1 -0
- package/dist/checks/tree.js +52 -0
- package/dist/checks/tree.js.map +1 -0
- package/dist/checks/wiring.d.ts +5 -0
- package/dist/checks/wiring.d.ts.map +1 -0
- package/dist/checks/wiring.js +163 -0
- package/dist/checks/wiring.js.map +1 -0
- package/dist/commands/commit-message.d.ts +11 -0
- package/dist/commands/commit-message.d.ts.map +1 -0
- package/dist/commands/commit-message.js +57 -0
- package/dist/commands/commit-message.js.map +1 -0
- package/dist/commands/format.d.ts +4 -0
- package/dist/commands/format.d.ts.map +1 -0
- package/dist/commands/format.js +35 -0
- package/dist/commands/format.js.map +1 -0
- package/dist/commands/hooks.d.ts +9 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +57 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +92 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/sync.d.ts +4 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +30 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/verify.d.ts +18 -0
- package/dist/commands/verify.d.ts.map +1 -0
- package/dist/commands/verify.js +96 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/context.d.ts +157 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +345 -0
- package/dist/context.js.map +1 -0
- package/dist/exec.d.ts +80 -0
- package/dist/exec.d.ts.map +1 -0
- package/dist/exec.js +117 -0
- package/dist/exec.js.map +1 -0
- package/dist/gates.d.ts +61 -0
- package/dist/gates.d.ts.map +1 -0
- package/dist/gates.js +298 -0
- package/dist/gates.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/report.d.ts +40 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +224 -0
- package/dist/report.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Working-tree integrity: a fingerprint taken before the gates, and checked again after. Verification must
|
|
2
|
+
// judge the tree the project committed, not one a gate quietly rewrote as a side effect. A formatter or
|
|
3
|
+
// a code generator that edits during `verify` would otherwise let a build pass on content that is not in
|
|
4
|
+
// the repository, and the next clean checkout would fail.
|
|
5
|
+
import { createHash } from 'node:crypto';
|
|
6
|
+
import { readFileSync, statSync } from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { trackedFiles } from '../context.js';
|
|
9
|
+
import { failed, passed } from '../exec.js';
|
|
10
|
+
const fingerprint = (context) => {
|
|
11
|
+
const hash = createHash('sha256');
|
|
12
|
+
for (const file of trackedFiles(context.root)) {
|
|
13
|
+
const full = path.join(context.root, file);
|
|
14
|
+
hash.update(file);
|
|
15
|
+
try {
|
|
16
|
+
hash.update(statSync(full).isFile() ? readFileSync(full) : Buffer.alloc(0));
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// A tracked-but-deleted path contributes its name only, so deleting one still changes the digest.
|
|
20
|
+
hash.update('<absent>');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return hash.digest('hex');
|
|
24
|
+
};
|
|
25
|
+
// The fingerprint is taken by one gate and compared by another, so it must outlive both calls, and
|
|
26
|
+
// there is no channel between gates to carry it as a value.
|
|
27
|
+
// eslint-disable-next-line functional/no-let -- must outlive two separate gate invocations
|
|
28
|
+
let snapshot;
|
|
29
|
+
/** Record the tree state before the gates that could modify it run. */
|
|
30
|
+
// Enough of the fingerprint to compare by eye; the full value is never the useful part of a report.
|
|
31
|
+
const FINGERPRINT_PREVIEW = 12;
|
|
32
|
+
export const treeSnapshot = (context) => {
|
|
33
|
+
// Recording state the later tree-verify gate reads is this gate's whole purpose.
|
|
34
|
+
// eslint-disable-next-line unicorn/no-top-level-assignment-in-function -- see the binding above
|
|
35
|
+
snapshot = fingerprint(context);
|
|
36
|
+
return passed(`tree fingerprint recorded (${snapshot.slice(0, FINGERPRINT_PREVIEW)})`);
|
|
37
|
+
};
|
|
38
|
+
/** Verify no gate modified a tracked file during verification. */
|
|
39
|
+
export const treeVerify = (context) => {
|
|
40
|
+
if (snapshot === undefined) {
|
|
41
|
+
return passed('no tree snapshot was taken, so there is nothing to compare');
|
|
42
|
+
}
|
|
43
|
+
const current = fingerprint(context);
|
|
44
|
+
return current === snapshot
|
|
45
|
+
? passed('the working tree is unchanged since verification began')
|
|
46
|
+
: failed('a gate modified a tracked file during verification', [
|
|
47
|
+
'run `ploaness format`, review the result, and commit it before verifying',
|
|
48
|
+
`expected ${snapshot.slice(0, FINGERPRINT_PREVIEW)} but found ${current.slice(0, FINGERPRINT_PREVIEW)}`,
|
|
49
|
+
'git status will show which files changed',
|
|
50
|
+
]);
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tree.js","sourceRoot":"","sources":["../../src/checks/tree.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,wGAAwG;AACxG,yGAAyG;AACzG,0DAA0D;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAChD,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAgB,YAAY,EAAE,MAAM,eAAe,CAAA;AAC1D,OAAO,EAAE,MAAM,EAAmB,MAAM,EAAE,MAAM,YAAY,CAAA;AAE5D,MAAM,WAAW,GAAG,CAAC,OAAgB,EAAU,EAAE;IAC/C,MAAM,IAAI,GAAkC,UAAU,CAAC,QAAQ,CAAC,CAAA;IAChE,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,GAAW,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAClD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACjB,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7E,CAAC;QAAC,MAAM,CAAC;YACP,kGAAkG;YAClG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;QACzB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC3B,CAAC,CAAA;AAED,mGAAmG;AACnG,4DAA4D;AAC5D,2FAA2F;AAC3F,IAAI,QAA4B,CAAA;AAEhC,uEAAuE;AACvE,oGAAoG;AACpG,MAAM,mBAAmB,GAAW,EAAE,CAAA;AAEtC,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,OAAgB,EAAc,EAAE;IAC3D,iFAAiF;IACjF,gGAAgG;IAChG,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;IAC/B,OAAO,MAAM,CAAC,8BAA8B,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,GAAG,CAAC,CAAA;AACxF,CAAC,CAAA;AAED,kEAAkE;AAClE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAgB,EAAc,EAAE;IACzD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC,4DAA4D,CAAC,CAAA;IAC7E,CAAC;IACD,MAAM,OAAO,GAAW,WAAW,CAAC,OAAO,CAAC,CAAA;IAC5C,OAAO,OAAO,KAAK,QAAQ;QACzB,CAAC,CAAC,MAAM,CAAC,wDAAwD,CAAC;QAClE,CAAC,CAAC,MAAM,CAAC,oDAAoD,EAAE;YAC3D,0EAA0E;YAC1E,YAAY,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,cAAc,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,EAAE;YACvG,0CAA0C;SAC3C,CAAC,CAAA;AACR,CAAC,CAAA"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type Repository } from '../context.js';
|
|
2
|
+
import { type GateResult } from '../exec.js';
|
|
3
|
+
/** Verify the repository and each of its members have installed ploaness exactly as ploaness dictates. */
|
|
4
|
+
export declare const wiring: (repository: Repository) => GateResult;
|
|
5
|
+
//# sourceMappingURL=wiring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiring.d.ts","sourceRoot":"","sources":["../../src/checks/wiring.ts"],"names":[],"mappings":"AAuBA,OAAO,EAEL,KAAK,UAAU,EAKhB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAU,KAAK,UAAU,EAAU,MAAM,YAAY,CAAA;AAwM5D,0GAA0G;AAC1G,eAAO,MAAM,MAAM,GAAI,YAAY,UAAU,KAAG,UAyB/C,CAAA"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
// The anti-bypass gate. Reads the consumer's own files and fails when ploaness has been disarmed. The
|
|
2
|
+
// rules are pure and live in @ploaness/governance; this file supplies the I/O and the expectations.
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { asOptionalText, asRecord, asStringRecord, declaredDependencies, findPackageWiringViolations, findRepositoryWiringViolations, findServerUrlCollisions, findUngovernedProjects, hasRuntime, isArray, isRecord, memberKindOf, ROOT_MEMBER_PATH, readKey, requiredBiomeFiles, } from '@ploaness/governance';
|
|
5
|
+
import { readJson, readPins, readText, shippedDirectory, } from '../context.js';
|
|
6
|
+
import { failed, passed } from '../exec.js';
|
|
7
|
+
// Libraries the consumer's own specs import. They cannot move into the harness: under the strict pnpm
|
|
8
|
+
// layout a consumer spec could not resolve `import { describe } from "vitest"` if vitest were only a
|
|
9
|
+
// dependency of ploaness. So the project declares them and ploaness pins the version.
|
|
10
|
+
// Only libraries a consumer's own specs IMPORT belong here. Under pnpm's strict layout a spec cannot
|
|
11
|
+
// resolve `vitest` unless the project declares it, so these cannot move into the harness and are pinned
|
|
12
|
+
// instead. A package the harness merely resolves for itself - the React plugin, for instance - must NOT
|
|
13
|
+
// appear: declaring it in the consumer would be an unused dependency, which the knip gate rightly fails.
|
|
14
|
+
// Every package whose version changes what a gate does. The standard pins the toolchain to an exact
|
|
15
|
+
// version so an upstream release cannot change a verdict while the project stays unchanged - and a
|
|
16
|
+
// caret range on any of these is exactly that. The expected version is read from ploaness's own
|
|
17
|
+
// manifests below, so there is no second literal to drift.
|
|
18
|
+
const TEST_LIBRARY_NAMES = [
|
|
19
|
+
'vitest',
|
|
20
|
+
'@vitest/coverage-v8',
|
|
21
|
+
'typescript',
|
|
22
|
+
'jsdom',
|
|
23
|
+
'fast-check',
|
|
24
|
+
'@testing-library/react',
|
|
25
|
+
'@testing-library/jest-dom',
|
|
26
|
+
'@testing-library/user-event',
|
|
27
|
+
'@playwright/test',
|
|
28
|
+
'@axe-core/playwright',
|
|
29
|
+
// The end-to-end gate injects `--import=tsx/esm`, so the project's tsx decides whether that gate can
|
|
30
|
+
// start at all. ploaness depended on a package it neither required nor pinned until this was added.
|
|
31
|
+
'tsx',
|
|
32
|
+
];
|
|
33
|
+
const shippedFile = (packageName, fileName) => path.join(shippedDirectory(packageName), fileName);
|
|
34
|
+
const pinGroups = () => {
|
|
35
|
+
const groups = readPins()['groups'];
|
|
36
|
+
return isArray(groups)
|
|
37
|
+
? groups.flatMap((group) => {
|
|
38
|
+
const record = asRecord(group);
|
|
39
|
+
return isRecord(record['versions'])
|
|
40
|
+
? [
|
|
41
|
+
{
|
|
42
|
+
requiredFor: asStringArray(record['requiredFor']),
|
|
43
|
+
versions: asStringRecord(record['versions']),
|
|
44
|
+
},
|
|
45
|
+
]
|
|
46
|
+
: [];
|
|
47
|
+
})
|
|
48
|
+
: [];
|
|
49
|
+
};
|
|
50
|
+
/** Every pinned version, whether the group that holds it is required or merely matched. */
|
|
51
|
+
const ownedVersions = () => Object.assign({}, ...pinGroups().map((group) => group.versions));
|
|
52
|
+
// Where a pinned version is read from. The harness installs these itself, so its own manifests are the
|
|
53
|
+
// single source of the expectation and a harness bump moves it in exactly one place. Both packages are
|
|
54
|
+
// consulted because the split between them is an internal packaging detail: vitest is declared by the
|
|
55
|
+
// config package that presets it, the coverage provider by the CLI package that runs it. Reading only
|
|
56
|
+
// one would leave the other name in TEST_LIBRARY_NAMES pinning nothing at all, silently.
|
|
57
|
+
const asStringArray = (value) => isArray(value) ? value.filter((entry) => typeof entry === 'string') : [];
|
|
58
|
+
const VERSION_SOURCES = ['@ploaness/config', '@ploaness/cli'];
|
|
59
|
+
const declaredBy = (packageName) => {
|
|
60
|
+
const manifest = readJson(shippedFile(packageName, 'package.json'));
|
|
61
|
+
return asRecord(readKey(manifest, 'dependencies'));
|
|
62
|
+
};
|
|
63
|
+
// The packages a project's specs import, so every project must declare them. The rest of the pinned set
|
|
64
|
+
// is matched when declared and never forced on a project that has no use for it. The two Playwright
|
|
65
|
+
// packages are here because ploaness now ships a spec that imports them: the managed accessibility
|
|
66
|
+
// sweep is not optional, so neither is resolving what it imports.
|
|
67
|
+
// A pin in a required group is what makes a project declare the package. A pin in any other group is
|
|
68
|
+
// matched when the project declares it and forced on nobody, which is how ploaness can own the version
|
|
69
|
+
// of a Postgres driver without deciding that every governed project uses Postgres.
|
|
70
|
+
// Only the packages this KIND of member must declare. A group names the kinds it applies to, so a
|
|
71
|
+
// shared library is not told to declare Payload - which would manufacture a dependency the dead-code
|
|
72
|
+
// gate then rightly reports as unused - and a frontend is not told to declare it either, which would
|
|
73
|
+
// make ploaness dictate an architecture rather than a version.
|
|
74
|
+
const requiredPackages = (kind) => new Set(pinGroups()
|
|
75
|
+
.filter((group) => group.requiredFor.includes(kind))
|
|
76
|
+
.flatMap((group) => Object.keys(group.versions)));
|
|
77
|
+
const expectedTestLibraries = () => {
|
|
78
|
+
// Built from one flat list of entries rather than by folding an object into itself: a later source
|
|
79
|
+
// still wins, because `fromEntries` keeps the last entry for a repeated key.
|
|
80
|
+
const declared = Object.fromEntries(VERSION_SOURCES.flatMap((source) => Object.entries(declaredBy(source))));
|
|
81
|
+
const fromDependencies = asStringRecord(Object.fromEntries(TEST_LIBRARY_NAMES.map((name) => [name, declared[name]])));
|
|
82
|
+
return { ...fromDependencies, ...ownedVersions() };
|
|
83
|
+
};
|
|
84
|
+
// A member's own required set. Only a member that serves an application needs the browser packages: a
|
|
85
|
+
// library has no page to sweep, and requiring them would manufacture dependencies the dead-code gate
|
|
86
|
+
// then rightly reports as unused.
|
|
87
|
+
const BROWSER_LIBRARIES = new Set([
|
|
88
|
+
'@playwright/test',
|
|
89
|
+
'@axe-core/playwright',
|
|
90
|
+
'tsx',
|
|
91
|
+
]);
|
|
92
|
+
// The specs of every governed package import these, so pnpm's strict layout means every package must
|
|
93
|
+
// declare them. The browser three are added only where there is an application to drive.
|
|
94
|
+
const SUITE_LIBRARIES = new Set([
|
|
95
|
+
'vitest',
|
|
96
|
+
'@vitest/coverage-v8',
|
|
97
|
+
'typescript',
|
|
98
|
+
]);
|
|
99
|
+
const requiredFor = (kind) => new Set([
|
|
100
|
+
...SUITE_LIBRARIES,
|
|
101
|
+
...(hasRuntime(kind) ? BROWSER_LIBRARIES : []),
|
|
102
|
+
...requiredPackages(kind),
|
|
103
|
+
]);
|
|
104
|
+
const memberViolations = (member) => {
|
|
105
|
+
const kind = memberKindOf(member.packageJson);
|
|
106
|
+
return findPackageWiringViolations({
|
|
107
|
+
packageJson: member.packageJson,
|
|
108
|
+
kind,
|
|
109
|
+
isNestedMember: member.path !== ROOT_MEMBER_PATH,
|
|
110
|
+
nestedMembers: member.siblingPaths,
|
|
111
|
+
requiredBiomeFiles: requiredBiomeFiles(member.settings.sourceRoots, kind, member.settings.generatedArtefacts),
|
|
112
|
+
eslintConfig: readText(path.join(member.root, 'eslint.config.mjs')),
|
|
113
|
+
vitestConfig: readText(path.join(member.root, 'vitest.config.mts')),
|
|
114
|
+
playwrightConfig: readText(path.join(member.root, 'playwright.config.ts')),
|
|
115
|
+
declaredExclusions: member.settings.declaredExclusions,
|
|
116
|
+
biomeConfig: readText(path.join(member.root, 'biome.json')),
|
|
117
|
+
tsconfig: readText(path.join(member.root, 'tsconfig.json')),
|
|
118
|
+
expectedTestLibraries: expectedTestLibraries(),
|
|
119
|
+
requiredTestLibraries: requiredFor(kind),
|
|
120
|
+
payloadVersion: ownedVersions()['payload'],
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
// A finding names the member it came from only when there is more than one, so a single-package project
|
|
124
|
+
// reads exactly the findings it always did.
|
|
125
|
+
const locate = (member, isSolo, violation) => isSolo
|
|
126
|
+
? `${violation.location}: ${violation.reason}`
|
|
127
|
+
: `${member.path}/${violation.location}: ${violation.reason}`;
|
|
128
|
+
const shapeOf = (member) => ({
|
|
129
|
+
path: member.path,
|
|
130
|
+
isPayload: member.isPayload,
|
|
131
|
+
sourceRoots: member.settings.sourceRoots,
|
|
132
|
+
});
|
|
133
|
+
// The rules that need the member LIST rather than any one manifest. Both describe a way a workspace can
|
|
134
|
+
// be wrong that no single package can see: a project quietly left ungoverned, and two applications that
|
|
135
|
+
// would drive the same origin and sweep each other.
|
|
136
|
+
const acrossMembers = (repository) => [
|
|
137
|
+
...findUngovernedProjects(repository.projects, repository.members.map((member) => shapeOf(member))).map((reason) => ({ location: 'pnpm-workspace.yaml', reason })),
|
|
138
|
+
...findServerUrlCollisions(repository.members
|
|
139
|
+
.filter((member) => hasRuntime(memberKindOf(member.packageJson)))
|
|
140
|
+
.map((member) => ({ path: member.path, serverUrl: member.settings.serverUrl }))).map((reason) => ({ location: 'package.json ploaness', reason })),
|
|
141
|
+
];
|
|
142
|
+
const declaredEverywhere = (repository) => Object.assign({}, ...repository.members.map((member) => declaredDependencies(member.packageJson)));
|
|
143
|
+
/** Verify the repository and each of its members have installed ploaness exactly as ploaness dictates. */
|
|
144
|
+
export const wiring = (repository) => {
|
|
145
|
+
const isSolo = repository.members.length <= 1;
|
|
146
|
+
const findings = [
|
|
147
|
+
...findRepositoryWiringViolations({
|
|
148
|
+
packageJson: repository.packageJson,
|
|
149
|
+
workspaceFile: repository.workspaceFile,
|
|
150
|
+
declaredExclusions: repository.settings.declaredExclusions,
|
|
151
|
+
expectedTestLibraries: expectedTestLibraries(),
|
|
152
|
+
requiredPackageManager: asOptionalText(readPins()['packageManager']),
|
|
153
|
+
requiredEngines: asStringRecord(readPins()['engines']),
|
|
154
|
+
declaredAcrossMembers: declaredEverywhere(repository),
|
|
155
|
+
repositoryFindings: acrossMembers(repository),
|
|
156
|
+
}).map((violation) => `${violation.location}: ${violation.reason}`),
|
|
157
|
+
...repository.members.flatMap((member) => memberViolations(member).map((violation) => locate(member, isSolo, violation))),
|
|
158
|
+
];
|
|
159
|
+
return findings.length > 0
|
|
160
|
+
? failed(`${String(findings.length)} wiring defect(s); the harness is not installed as ploaness requires`, findings)
|
|
161
|
+
: passed('ploaness is wired into the project as required');
|
|
162
|
+
};
|
|
163
|
+
//# sourceMappingURL=wiring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wiring.js","sourceRoot":"","sources":["../../src/checks/wiring.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Context } from '../context.js';
|
|
2
|
+
/**
|
|
3
|
+
* The commit-message entry point, in three modes: a message file, a revision range, or the whole
|
|
4
|
+
* history.
|
|
5
|
+
* @param context the resolved project environment.
|
|
6
|
+
* @param mode a message file path, `--range`, or `--all`.
|
|
7
|
+
* @param value the revision range, when mode is `--range`.
|
|
8
|
+
* @returns the process exit code.
|
|
9
|
+
*/
|
|
10
|
+
export declare const commitMessage: (context: Context, mode: string | undefined, value: string | undefined) => number;
|
|
11
|
+
//# sourceMappingURL=commit-message.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-message.d.ts","sourceRoot":"","sources":["../../src/commands/commit-message.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAmC5C;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GACxB,SAAS,OAAO,EAChB,MAAM,MAAM,GAAG,SAAS,EACxB,OAAO,MAAM,GAAG,SAAS,KACxB,MAYF,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// The commit-message validator. It is a plain command rather than a git hook: ploaness enforces no
|
|
2
|
+
// hooks, because a hook is local, opt-in, and bypassable with --no-verify, so CI is the only place a
|
|
3
|
+
// rule can actually be held. `commit-history` runs the same policy as a gate.
|
|
4
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
5
|
+
import { OWNED_HISTORY_REVISIONS } from '@ploaness/governance';
|
|
6
|
+
import { commitHistory, commitMessageProblems } from '../checks/history.js';
|
|
7
|
+
/** Report a run of findings under a closing line, and return the failing exit code. */
|
|
8
|
+
const reportProblems = (findings, summary) => {
|
|
9
|
+
for (const finding of findings) {
|
|
10
|
+
console.error(finding);
|
|
11
|
+
}
|
|
12
|
+
console.error(`\n${summary}. See .ploaness/agent-guide.md.`);
|
|
13
|
+
return 1;
|
|
14
|
+
};
|
|
15
|
+
/** Check the messages already in the history, either a range or all of it. */
|
|
16
|
+
const checkHistory = (context, revisions) => {
|
|
17
|
+
const result = commitHistory(context, revisions);
|
|
18
|
+
if (!result.ok) {
|
|
19
|
+
return reportProblems(result.findings, result.summary);
|
|
20
|
+
}
|
|
21
|
+
console.info(result.summary);
|
|
22
|
+
return 0;
|
|
23
|
+
};
|
|
24
|
+
/** Check one pending message, read from the file the author points at. */
|
|
25
|
+
const checkPending = (context, file) => {
|
|
26
|
+
// A hook invokes this with a path git supplies, and a mistyped mode reaches it as a filename. Either
|
|
27
|
+
// way an absent file is a thing to say, not a stack trace to print.
|
|
28
|
+
if (!existsSync(file)) {
|
|
29
|
+
return reportProblems([`no such file: ${file}`], 'the commit message could not be read');
|
|
30
|
+
}
|
|
31
|
+
const problems = commitMessageProblems(context, readFileSync(file, 'utf8'));
|
|
32
|
+
return problems.length > 0
|
|
33
|
+
? reportProblems(problems, `${String(problems.length)} commit-message problem(s)`)
|
|
34
|
+
: 0;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* The commit-message entry point, in three modes: a message file, a revision range, or the whole
|
|
38
|
+
* history.
|
|
39
|
+
* @param context the resolved project environment.
|
|
40
|
+
* @param mode a message file path, `--range`, or `--all`.
|
|
41
|
+
* @param value the revision range, when mode is `--range`.
|
|
42
|
+
* @returns the process exit code.
|
|
43
|
+
*/
|
|
44
|
+
export const commitMessage = (context, mode, value) => {
|
|
45
|
+
if (mode === undefined) {
|
|
46
|
+
console.error('usage: ploaness commit-message <message-file> | --range <base>..<head> | --all');
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
49
|
+
if (mode === '--all') {
|
|
50
|
+
return checkHistory(context, OWNED_HISTORY_REVISIONS);
|
|
51
|
+
}
|
|
52
|
+
if (mode === '--range') {
|
|
53
|
+
return checkHistory(context, [value ?? 'HEAD']);
|
|
54
|
+
}
|
|
55
|
+
return checkPending(context, mode);
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=commit-message.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commit-message.js","sourceRoot":"","sources":["../../src/commands/commit-message.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,qGAAqG;AACrG,8EAA8E;AAC9E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AAC9D,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAI3E,uFAAuF;AACvF,MAAM,cAAc,GAAG,CAAC,QAA2B,EAAE,OAAe,EAAU,EAAE;IAC9E,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACxB,CAAC;IACD,OAAO,CAAC,KAAK,CAAC,KAAK,OAAO,iCAAiC,CAAC,CAAA;IAC5D,OAAO,CAAC,CAAA;AACV,CAAC,CAAA;AAED,8EAA8E;AAC9E,MAAM,YAAY,GAAG,CAAC,OAAgB,EAAE,SAA4B,EAAU,EAAE;IAC9E,MAAM,MAAM,GAAe,aAAa,CAAC,OAAO,EAAE,SAAS,CAAC,CAAA;IAC5D,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO,cAAc,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;IACxD,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;IAC5B,OAAO,CAAC,CAAA;AACV,CAAC,CAAA;AAED,0EAA0E;AAC1E,MAAM,YAAY,GAAG,CAAC,OAAgB,EAAE,IAAY,EAAU,EAAE;IAC9D,qGAAqG;IACrG,oEAAoE;IACpE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,cAAc,CAAC,CAAC,iBAAiB,IAAI,EAAE,CAAC,EAAE,sCAAsC,CAAC,CAAA;IAC1F,CAAC;IACD,MAAM,QAAQ,GAAsB,qBAAqB,CAAC,OAAO,EAAE,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAA;IAC9F,OAAO,QAAQ,CAAC,MAAM,GAAG,CAAC;QACxB,CAAC,CAAC,cAAc,CAAC,QAAQ,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,4BAA4B,CAAC;QAClF,CAAC,CAAC,CAAC,CAAA;AACP,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,OAAgB,EAChB,IAAwB,EACxB,KAAyB,EACjB,EAAE;IACV,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,gFAAgF,CAAC,CAAA;QAC/F,OAAO,CAAC,CAAA;IACV,CAAC;IACD,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;QACrB,OAAO,YAAY,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAA;IACvD,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,YAAY,CAAC,OAAO,EAAE,CAAC,KAAK,IAAI,MAAM,CAAC,CAAC,CAAA;IACjD,CAAC;IACD,OAAO,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;AACpC,CAAC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/commands/format.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,eAAe,CAAA;AA2BvD,6FAA6F;AAC7F,eAAO,MAAM,MAAM,GAAI,YAAY,UAAU,KAAG,MAO/C,CAAA"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Formatting. It writes; it never judges. Review what it
|
|
2
|
+
// changed before committing, because a formatter that runs unattended is how unreviewed edits enter a
|
|
3
|
+
// repository.
|
|
4
|
+
import { biomeWrite, eslintWrite } from '../checks/toolchain.js';
|
|
5
|
+
// Each member is formatted against its own configuration. Both tools resolve a relative glob against
|
|
6
|
+
// the config that declares it, so running once at the repository root would format one member's files
|
|
7
|
+
// by another member's rules - or, where the root holds no configuration of its own, by none.
|
|
8
|
+
const formatMember = (member, isSolo) => {
|
|
9
|
+
if (!isSolo) {
|
|
10
|
+
console.info(`\n${member.path}`);
|
|
11
|
+
}
|
|
12
|
+
// Each invocation lives beside the gate that judges what it writes, so the two cannot disagree about
|
|
13
|
+
// which files the tool touches. Both functions once carried their own copy of it, and the ESLint half
|
|
14
|
+
// stayed that way a tool longer: it was told `.` and nothing else, so a run started at a workspace
|
|
15
|
+
// root descended into every member and applied the ROOT's rules to files that member excludes. It
|
|
16
|
+
// rewrote a generated import map, and no gate could report it - each member's own run correctly
|
|
17
|
+
// ignores that file, which is exactly why the writer had to be told where to stop.
|
|
18
|
+
const biome = biomeWrite(member);
|
|
19
|
+
console.info(biome.output);
|
|
20
|
+
const eslint = eslintWrite(member);
|
|
21
|
+
if (eslint.output.length > 0) {
|
|
22
|
+
console.info(eslint.output);
|
|
23
|
+
}
|
|
24
|
+
// ESLint exits non-zero for findings it cannot fix. Those are for `ploaness verify` to report, so
|
|
25
|
+
// formatting succeeds as long as Biome could write.
|
|
26
|
+
return biome.code === 0 ? 0 : 1;
|
|
27
|
+
};
|
|
28
|
+
/** Apply Biome's formatting and safe fixes, then ESLint's fixable rules, in every member. */
|
|
29
|
+
export const format = (repository) => {
|
|
30
|
+
const isSolo = repository.members.length <= 1;
|
|
31
|
+
const codes = repository.members.map((member) => formatMember(member, isSolo));
|
|
32
|
+
console.info('\nFormatting applied. Review the changes before committing them.');
|
|
33
|
+
return codes.some((code) => code !== 0) ? 1 : 0;
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/commands/format.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,sGAAsG;AACtG,cAAc;AACd,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAIhE,qGAAqG;AACrG,sGAAsG;AACtG,6FAA6F;AAC7F,MAAM,YAAY,GAAG,CAAC,MAAc,EAAE,MAAe,EAAU,EAAE;IAC/D,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IAClC,CAAC;IACD,qGAAqG;IACrG,sGAAsG;IACtG,mGAAmG;IACnG,kGAAkG;IAClG,gGAAgG;IAChG,mFAAmF;IACnF,MAAM,KAAK,GAAc,UAAU,CAAC,MAAM,CAAC,CAAA;IAC3C,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAC1B,MAAM,MAAM,GAAc,WAAW,CAAC,MAAM,CAAC,CAAA;IAC7C,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC7B,CAAC;IACD,kGAAkG;IAClG,oDAAoD;IACpD,OAAO,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACjC,CAAC,CAAA;AAED,6FAA6F;AAC7F,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,UAAsB,EAAU,EAAE;IACvD,MAAM,MAAM,GAAY,UAAU,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,CAAA;IACtD,MAAM,KAAK,GAAsB,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAc,EAAU,EAAE,CACjF,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAC7B,CAAA;IACD,OAAO,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAA;IAChF,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAY,EAAW,EAAE,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAClE,CAAC,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Context } from '../context.js';
|
|
2
|
+
/** The pre-commit hook: formatting on staged files, then a full type check. */
|
|
3
|
+
export declare const precommit: (context: Context) => number;
|
|
4
|
+
/**
|
|
5
|
+
* The commit-message entry point, in three modes: a pending message file (the commit-msg hook), a
|
|
6
|
+
* revision range, or the whole history.
|
|
7
|
+
*/
|
|
8
|
+
export declare const commitMessage: (context: Context, mode: string | undefined, value: string | undefined) => number;
|
|
9
|
+
//# sourceMappingURL=hooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/commands/hooks.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,OAAO,EAAe,MAAM,eAAe,CAAA;AAGzD,+EAA+E;AAC/E,eAAO,MAAM,SAAS,GAAI,SAAS,OAAO,KAAG,MAuB5C,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,aAAa,GACxB,SAAS,OAAO,EAChB,MAAM,MAAM,GAAG,SAAS,EACxB,OAAO,MAAM,GAAG,SAAS,KACxB,MA2BF,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// The git-hook entry points. A hook must be fast enough that a developer does not learn to pass
|
|
2
|
+
// --no-verify, so the pre-commit path runs only the two checks that catch most of what a commit gets
|
|
3
|
+
// wrong, and CI remains the backstop for everything else.
|
|
4
|
+
import { readFileSync } from 'node:fs';
|
|
5
|
+
import { commitHistory, commitMessageProblems } from '../checks/history.js';
|
|
6
|
+
import { resolveTool } from '../context.js';
|
|
7
|
+
import { runNode } from '../exec.js';
|
|
8
|
+
/** The pre-commit hook: formatting on staged files, then a full type check. */
|
|
9
|
+
export const precommit = (context) => {
|
|
10
|
+
const biome = runNode(resolveTool('@biomejs/biome', 'biome'), ['check', '--staged', '--no-errors-on-unmatched'], { cwd: context.root });
|
|
11
|
+
if (biome.code !== 0) {
|
|
12
|
+
console.error(biome.output);
|
|
13
|
+
return 1;
|
|
14
|
+
}
|
|
15
|
+
const types = runNode(resolveTool('typescript', 'tsc'), ['--noEmit', '-p', 'tsconfig.json'], {
|
|
16
|
+
cwd: context.root,
|
|
17
|
+
});
|
|
18
|
+
if (types.code !== 0) {
|
|
19
|
+
console.error(types.output);
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
console.info('ploaness precommit: staged formatting and the type check are clean.');
|
|
23
|
+
return 0;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* The commit-message entry point, in three modes: a pending message file (the commit-msg hook), a
|
|
27
|
+
* revision range, or the whole history.
|
|
28
|
+
*/
|
|
29
|
+
export const commitMessage = (context, mode, value) => {
|
|
30
|
+
if (mode === undefined) {
|
|
31
|
+
console.error('usage: ploaness commit-message <message-file> | --range <base>..<head> | --all');
|
|
32
|
+
return 1;
|
|
33
|
+
}
|
|
34
|
+
if (mode === '--all' || mode === '--range') {
|
|
35
|
+
const revisions = mode === '--all' ? ['HEAD'] : [value ?? 'HEAD'];
|
|
36
|
+
const result = commitHistory(context, revisions);
|
|
37
|
+
if (!result.ok) {
|
|
38
|
+
for (const finding of result.findings) {
|
|
39
|
+
console.error(finding);
|
|
40
|
+
}
|
|
41
|
+
console.error(`\n${result.summary}. See .ploaness/agent-guide.md.`);
|
|
42
|
+
return 1;
|
|
43
|
+
}
|
|
44
|
+
console.info(result.summary);
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
const problems = commitMessageProblems(context, readFileSync(mode, 'utf8'));
|
|
48
|
+
if (problems.length > 0) {
|
|
49
|
+
for (const problem of problems) {
|
|
50
|
+
console.error(problem);
|
|
51
|
+
}
|
|
52
|
+
console.error(`\n${problems.length} commit-message problem(s). See .ploaness/agent-guide.md.`);
|
|
53
|
+
return 1;
|
|
54
|
+
}
|
|
55
|
+
return 0;
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/commands/hooks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAU,UAAU,EAAE,MAAM,eAAe,CAAA;AAiGvD,2EAA2E;AAC3E,eAAO,MAAM,IAAI,GAAI,YAAY,UAAU,KAAG,MAqB7C,CAAA"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// `ploaness init`: write the wiring a consumer needs, so adopting the harness is two commands rather
|
|
2
|
+
// than a page of copied configuration. Nothing here is magic: every file it writes is one the wiring
|
|
3
|
+
// gate will afterwards require, and it never overwrites a file that already exists.
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { asRecord, memberKindOf, parseJsonc, REQUIRED_SCRIPTS, ROOT_MEMBER_PATH, requiredBiomeFiles, tsconfigPathsFor, wiringTargetsFor, } from '@ploaness/governance';
|
|
7
|
+
import { hasSeededFile } from '../checks/assets.js';
|
|
8
|
+
import { sync } from './sync.js';
|
|
9
|
+
// Every stub is rendered from the same table the wiring rule reads. Writing either side's literal by
|
|
10
|
+
// hand is what once scaffolded a project the gate then failed.
|
|
11
|
+
const reexportStub = (specifier) => `import ploaness from '${specifier}'\n\nexport default ploaness\n`;
|
|
12
|
+
// The indent every JSON file ploaness writes uses, matching the shipped formatter setting.
|
|
13
|
+
const JSON_INDENT = 2;
|
|
14
|
+
// Biome resolves a relative glob against the config that declares it, so the file-selection block has to
|
|
15
|
+
// sit at each member's root even though ploaness owns its contents. The wiring gate enforces it verbatim.
|
|
16
|
+
const biomeStub = (member, kind, targets) => `${JSON.stringify({
|
|
17
|
+
// A nested configuration has to say it is not a root, or Biome refuses the whole tree.
|
|
18
|
+
...(member.path !== ROOT_MEMBER_PATH && { root: false }),
|
|
19
|
+
extends: [targets.biomeExtends],
|
|
20
|
+
files: requiredBiomeFiles(member.settings.sourceRoots, kind, member.settings.generatedArtefacts),
|
|
21
|
+
}, null, JSON_INDENT)}\n`;
|
|
22
|
+
// Only a Payload member has a Payload config to alias. The rest of the map is the project's own, which
|
|
23
|
+
// is why `paths` is one of the few compiler options a member may set for itself.
|
|
24
|
+
const pathAliases = (kind) => kind === 'payload'
|
|
25
|
+
? { '@/*': ['./src/*'], '@payload-config': ['./src/payload.config.ts'] }
|
|
26
|
+
: { '@/*': ['./src/*'] };
|
|
27
|
+
const tsconfigStub = (kind, targets, nestedMembers) => `${JSON.stringify({
|
|
28
|
+
extends: targets.tsconfigExtends,
|
|
29
|
+
compilerOptions: { paths: pathAliases(kind) },
|
|
30
|
+
...tsconfigPathsFor(targets.tsconfigPaths, nestedMembers),
|
|
31
|
+
}, null, JSON_INDENT)}\n`;
|
|
32
|
+
const stubs = (member) => {
|
|
33
|
+
const kind = memberKindOf(member.packageJson);
|
|
34
|
+
const targets = wiringTargetsFor(kind);
|
|
35
|
+
return {
|
|
36
|
+
'eslint.config.mjs': reexportStub(targets.eslintSpecifier),
|
|
37
|
+
'biome.json': biomeStub(member, kind, targets),
|
|
38
|
+
'tsconfig.json': tsconfigStub(kind, targets, member.siblingPaths),
|
|
39
|
+
'vitest.config.mts': reexportStub(targets.vitestSpecifier),
|
|
40
|
+
// A member with no application is not given a browser configuration, because the rule does not ask
|
|
41
|
+
// it for one.
|
|
42
|
+
...(targets.playwrightSpecifier !== undefined && {
|
|
43
|
+
'playwright.config.ts': reexportStub(targets.playwrightSpecifier),
|
|
44
|
+
}),
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
const patchPackageJson = (repository) => {
|
|
48
|
+
const file = path.join(repository.root, 'package.json');
|
|
49
|
+
if (!existsSync(file)) {
|
|
50
|
+
return ['package.json is missing, so the scripts were not written'];
|
|
51
|
+
}
|
|
52
|
+
const read = parseJsonc(readFileSync(file, 'utf8'));
|
|
53
|
+
if (read.problem !== undefined) {
|
|
54
|
+
return [`package.json is not valid JSON (${read.problem}), so the scripts were not written`];
|
|
55
|
+
}
|
|
56
|
+
const parsed = asRecord(read.value);
|
|
57
|
+
const scripts = {
|
|
58
|
+
...asRecord(parsed['scripts']),
|
|
59
|
+
...REQUIRED_SCRIPTS,
|
|
60
|
+
};
|
|
61
|
+
const updated = { ...parsed, scripts };
|
|
62
|
+
writeFileSync(file, `${JSON.stringify(updated, null, JSON_INDENT)}\n`);
|
|
63
|
+
return ['package.json: wrote the ploaness scripts'];
|
|
64
|
+
};
|
|
65
|
+
// The scripts are written once, at the repository root: `pnpm run verify` is invoked there and fans out
|
|
66
|
+
// from there, so a copy inside every member would be several entry points to one run.
|
|
67
|
+
const memberNotes = (member, isSolo) => {
|
|
68
|
+
const label = isSolo || member.path === ROOT_MEMBER_PATH ? '' : `${member.path}/`;
|
|
69
|
+
return Object.entries(stubs(member)).map(([file, body]) => hasSeededFile(member.root, file, body)
|
|
70
|
+
? `${label}${file}: written`
|
|
71
|
+
: `${label}${file}: left alone because it already exists`);
|
|
72
|
+
};
|
|
73
|
+
/** Scaffold the consumer-side wiring and materialise the managed files. */
|
|
74
|
+
export const init = (repository) => {
|
|
75
|
+
const isSolo = repository.members.length <= 1;
|
|
76
|
+
const notes = [
|
|
77
|
+
...patchPackageJson(repository),
|
|
78
|
+
...repository.members.flatMap((member) => memberNotes(member, isSolo)),
|
|
79
|
+
];
|
|
80
|
+
for (const note of notes) {
|
|
81
|
+
console.info(` ${note}`);
|
|
82
|
+
}
|
|
83
|
+
// `sync` writes the managed files and merges the write denial into the runtime settings.
|
|
84
|
+
const syncExit = sync(repository);
|
|
85
|
+
if (syncExit !== 0) {
|
|
86
|
+
return syncExit;
|
|
87
|
+
}
|
|
88
|
+
console.info('\nReview every change, then run `pnpm install` followed by `ploaness verify`. A pre-existing' +
|
|
89
|
+
'\nconfig was not overwritten: replace its contents with the ploaness stub yourself.');
|
|
90
|
+
return 0;
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAE/C,uEAAuE;AACvE,eAAO,MAAM,IAAI,GAAI,YAAY,UAAU,KAAG,MA4B7C,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// `ploaness sync`: the only command that writes managed files. Everything else reads them.
|
|
2
|
+
import { syncAssets } from '../checks/assets.js';
|
|
3
|
+
import { hasWrittenDenyRules } from '../checks/generated.js';
|
|
4
|
+
/** Materialise the managed files, then report exactly what changed. */
|
|
5
|
+
export const sync = (repository) => {
|
|
6
|
+
// The write denial is repaired here rather than in `init` alone, because the gate that requires it
|
|
7
|
+
// tells the project to run `ploaness sync`. A repair the advice cannot perform sends the project
|
|
8
|
+
// round a loop, which is the one thing a managed-file finding must never do.
|
|
9
|
+
const denialChange = hasWrittenDenyRules(repository)
|
|
10
|
+
? [{ action: 'wrote', path: '.claude/settings.json' }]
|
|
11
|
+
: [];
|
|
12
|
+
const changes = [...syncAssets(repository), ...denialChange];
|
|
13
|
+
if (changes.length === 0) {
|
|
14
|
+
console.info('ploaness sync: every managed path already matches the catalogue.');
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
console.info(`ploaness sync: ${String(changes.length)} change(s)`);
|
|
18
|
+
for (const change of changes) {
|
|
19
|
+
console.info(` ${change.action} ${change.path}`);
|
|
20
|
+
}
|
|
21
|
+
const refused = changes.filter((change) => change.action === 'refused');
|
|
22
|
+
if (refused.length > 0) {
|
|
23
|
+
console.error(`\n${String(refused.length)} file(s) carry duplicate, out-of-order, or non-leading ploaness markers.` +
|
|
24
|
+
'\nRepair the markers by hand, then run `ploaness sync` again.');
|
|
25
|
+
return 1;
|
|
26
|
+
}
|
|
27
|
+
console.info('\nReview these edits and commit them.');
|
|
28
|
+
return 0;
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAC3F,OAAO,EAAmB,UAAU,EAAE,MAAM,qBAAqB,CAAA;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAG5D,uEAAuE;AACvE,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,UAAsB,EAAU,EAAE;IACrD,mGAAmG;IACnG,iGAAiG;IACjG,6EAA6E;IAC7E,MAAM,YAAY,GAA0B,mBAAmB,CAAC,UAAU,CAAC;QACzE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC;QACtD,CAAC,CAAC,EAAE,CAAA;IACN,MAAM,OAAO,GAA0B,CAAC,GAAG,UAAU,CAAC,UAAU,CAAC,EAAE,GAAG,YAAY,CAAC,CAAA;IACnF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,kEAAkE,CAAC,CAAA;QAChF,OAAO,CAAC,CAAA;IACV,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,kBAAkB,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;IAClE,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,OAAO,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,CAAA;IACnD,CAAC;IACD,MAAM,OAAO,GAA0B,OAAO,CAAC,MAAM,CACnD,CAAC,MAAkB,EAAW,EAAE,CAAC,MAAM,CAAC,MAAM,KAAK,SAAS,CAC7D,CAAA;IACD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CACX,KAAK,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,0EAA0E;YACnG,+DAA+D,CAClE,CAAA;QACD,OAAO,CAAC,CAAA;IACV,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAA;IACrD,OAAO,CAAC,CAAA;AACV,CAAC,CAAA"}
|