@venizia/dev-configs 0.4.3 → 0.4.5

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.
Files changed (86) hide show
  1. package/LICENSE.md +2 -5
  2. package/README.md +15 -2
  3. package/dist/checks/catalog.d.ts +39 -0
  4. package/dist/checks/catalog.d.ts.map +1 -0
  5. package/dist/checks/catalog.js +94 -0
  6. package/dist/checks/catalog.js.map +1 -0
  7. package/dist/checks/index.d.ts +15 -0
  8. package/dist/checks/index.d.ts.map +1 -0
  9. package/dist/checks/index.js +31 -0
  10. package/dist/checks/index.js.map +1 -0
  11. package/dist/checks/module-cycles.d.ts +22 -0
  12. package/dist/checks/module-cycles.d.ts.map +1 -0
  13. package/dist/checks/module-cycles.js +149 -0
  14. package/dist/checks/module-cycles.js.map +1 -0
  15. package/dist/checks/purity/manifest.d.ts +94 -0
  16. package/dist/checks/purity/manifest.d.ts.map +1 -0
  17. package/dist/checks/purity/manifest.js +177 -0
  18. package/dist/checks/purity/manifest.js.map +1 -0
  19. package/dist/checks/purity/probe.d.ts +68 -0
  20. package/dist/checks/purity/probe.d.ts.map +1 -0
  21. package/dist/checks/purity/probe.js +295 -0
  22. package/dist/checks/purity/probe.js.map +1 -0
  23. package/dist/checks/purity/run.d.ts +47 -0
  24. package/dist/checks/purity/run.d.ts.map +1 -0
  25. package/dist/checks/purity/run.js +121 -0
  26. package/dist/checks/purity/run.js.map +1 -0
  27. package/dist/checks/split-report.d.ts +19 -0
  28. package/dist/checks/split-report.d.ts.map +1 -0
  29. package/dist/checks/split-report.js +123 -0
  30. package/dist/checks/split-report.js.map +1 -0
  31. package/dist/checks/test-controls.d.ts +46 -0
  32. package/dist/checks/test-controls.d.ts.map +1 -0
  33. package/dist/checks/test-controls.js +78 -0
  34. package/dist/checks/test-controls.js.map +1 -0
  35. package/dist/checks/test-dead-code.d.ts +18 -0
  36. package/dist/checks/test-dead-code.d.ts.map +1 -0
  37. package/dist/checks/test-dead-code.js +50 -0
  38. package/dist/checks/test-dead-code.js.map +1 -0
  39. package/dist/checks/test-mutate.d.ts +58 -0
  40. package/dist/checks/test-mutate.d.ts.map +1 -0
  41. package/dist/checks/test-mutate.js +165 -0
  42. package/dist/checks/test-mutate.js.map +1 -0
  43. package/dist/checks/test-weight.d.ts +64 -0
  44. package/dist/checks/test-weight.d.ts.map +1 -0
  45. package/dist/checks/test-weight.js +124 -0
  46. package/dist/checks/test-weight.js.map +1 -0
  47. package/dist/cli/check.d.ts +14 -0
  48. package/dist/cli/check.d.ts.map +1 -0
  49. package/dist/cli/check.js +319 -0
  50. package/dist/cli/check.js.map +1 -0
  51. package/dist/cli/codemod/const-classes.d.ts +80 -0
  52. package/dist/cli/codemod/const-classes.d.ts.map +1 -0
  53. package/dist/cli/codemod/const-classes.js +603 -0
  54. package/dist/cli/codemod/const-classes.js.map +1 -0
  55. package/dist/cli/codemod/drop-redundant-casts.d.ts +112 -0
  56. package/dist/cli/codemod/drop-redundant-casts.d.ts.map +1 -0
  57. package/dist/cli/codemod/drop-redundant-casts.js +780 -0
  58. package/dist/cli/codemod/drop-redundant-casts.js.map +1 -0
  59. package/dist/cli/codemod/index.d.ts +2 -1
  60. package/dist/cli/codemod/index.d.ts.map +1 -1
  61. package/dist/cli/codemod/index.js +25 -7
  62. package/dist/cli/codemod/index.js.map +1 -1
  63. package/dist/cli/codemod/model-settings.d.ts.map +1 -1
  64. package/dist/cli/codemod/model-settings.js +39 -2
  65. package/dist/cli/codemod/model-settings.js.map +1 -1
  66. package/dist/cli/index.js +9 -1
  67. package/dist/cli/index.js.map +1 -1
  68. package/dist/testing/index.d.ts +2 -0
  69. package/dist/testing/index.d.ts.map +1 -1
  70. package/dist/testing/index.js +2 -0
  71. package/dist/testing/index.js.map +1 -1
  72. package/dist/testing/repo-tmp.d.ts +12 -0
  73. package/dist/testing/repo-tmp.d.ts.map +1 -0
  74. package/dist/testing/repo-tmp.js +67 -0
  75. package/dist/testing/repo-tmp.js.map +1 -0
  76. package/dist/testing/spawn.helper.d.ts +13 -0
  77. package/dist/testing/spawn.helper.d.ts.map +1 -0
  78. package/dist/testing/spawn.helper.js +22 -0
  79. package/dist/testing/spawn.helper.js.map +1 -0
  80. package/dist/testing/tmpdir-preload.d.ts +2 -0
  81. package/dist/testing/tmpdir-preload.d.ts.map +1 -0
  82. package/dist/testing/tmpdir-preload.js +11 -0
  83. package/dist/testing/tmpdir-preload.js.map +1 -0
  84. package/package.json +9 -1
  85. package/src/tsconfig/tsconfig.base.json +5 -2
  86. package/src/tsconfig/tsconfig.common.json +2 -2
@@ -0,0 +1,319 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CheckCommand = void 0;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const catalog_1 = require("../checks/catalog");
7
+ const module_cycles_1 = require("../checks/module-cycles");
8
+ const run_1 = require("../checks/purity/run");
9
+ const split_report_1 = require("../checks/split-report");
10
+ const test_controls_1 = require("../checks/test-controls");
11
+ const test_dead_code_1 = require("../checks/test-dead-code");
12
+ const test_mutate_1 = require("../checks/test-mutate");
13
+ const test_weight_1 = require("../checks/test-weight");
14
+ const USAGE = `Usage: devz check <name> [options]
15
+
16
+ Checks:
17
+ purity [package] --claims <file> [--json] probe every entry claimed browser-pure (needs a build)
18
+ module-cycles <dir> [--max <n>] list the import cycles of a built ESM folder
19
+ split-report [--dist <dir>] [folder...] report hub files, stray types, missing barrels, long files, cycles (never a gate)
20
+ catalog [--glob <pattern>]... [--pinned <file>]... [--preset-prefix <prefix>]
21
+ guard dependency ranges against the root workspaces.catalog
22
+ test-weight [--top <n>] <junit.xml>... per-file time, cases, assertions, duplicates and skips from bun's JUnit report
23
+ test-dead-code list support files under __tests__ that no other file refers to (candidates)
24
+ test-mutate <ledger.json> replay a mutation ledger: the survivor must go red under each mutant
25
+ test-controls <registry.json> fail when a registered positive-control test is gone
26
+
27
+ purity
28
+ --claims <file> a module that exports PURITY_CLAIMS (the claims) and, optionally, PURITY_OPTIONS
29
+ [package] probe only the rows of this package; a token that matches nothing fails
30
+ --json print the verdict as JSON (no paths of this machine, no timing) instead of the report
31
+
32
+
33
+ test-weight, test-dead-code, test-mutate, test-controls: the tools of the trim-tests method, see
34
+ .agents/knowledge/process/trimming-tests.md in the IGNIS repository. Produce the JUnit files with
35
+ bun test ./src --parallel --reporter=junit --reporter-outfile=<file>.
36
+
37
+ module-cycles
38
+ <dir>, or --dist <dir> the built folder (dist/esm); exits 1 when more cycles than --max, 2 on a usage error
39
+
40
+ split-report
41
+ --dist <dir> the built ESM folder inside a package: dist/esm, or dist where one build is published (default dist)
42
+ [folder...] package folders to report; none scans every packages/* and examples/* that has a src/
43
+
44
+ catalog
45
+ --glob <pattern> a manifest glob beside packages/*/package.json (repeatable)
46
+ --pinned <file> a manifest outside the workspace: its catalogued ranges must equal the catalog's (repeatable)
47
+ --preset-prefix <p> files under this prefix are presets: catalog: is allowed in their peerDependencies
48
+
49
+ --help print this text and exit`;
50
+ /** A usage error: nothing was probed. */
51
+ const USAGE_ERROR_CODE = 2;
52
+ const parsePurityArguments = (args) => {
53
+ const parsed = { json: false };
54
+ for (let index = 0; index < args.length; index += 1) {
55
+ const arg = args[index];
56
+ if (arg === '--json') {
57
+ parsed.json = true;
58
+ continue;
59
+ }
60
+ if (arg === '--claims') {
61
+ const value = args[index + 1];
62
+ if (value === undefined || value.startsWith('--')) {
63
+ return '--claims needs a file';
64
+ }
65
+ parsed.claimsFile = value;
66
+ index += 1;
67
+ continue;
68
+ }
69
+ if (arg.startsWith('--')) {
70
+ return `unknown option "${arg}"`;
71
+ }
72
+ if (parsed.filter !== undefined) {
73
+ return `unexpected argument "${arg}"`;
74
+ }
75
+ parsed.filter = arg;
76
+ }
77
+ return parsed;
78
+ };
79
+ /** Splits repeatable `--name value` options and positionals; `undefined` for an option that lacks its value. */
80
+ const parseRepeatable = (opts) => {
81
+ const { args, repeatable, single } = opts;
82
+ const values = {};
83
+ const positionals = [];
84
+ for (let index = 0; index < args.length; index += 1) {
85
+ const arg = args[index];
86
+ if (arg.startsWith('--')) {
87
+ const name = arg.slice(2);
88
+ if (!repeatable.includes(name) && !single.includes(name)) {
89
+ return `unknown option "${arg}"`;
90
+ }
91
+ const value = args[index + 1];
92
+ if (value === undefined || value.startsWith('--')) {
93
+ return `${arg} needs a value`;
94
+ }
95
+ values[name] = [...(values[name] ?? []), value];
96
+ index += 1;
97
+ continue;
98
+ }
99
+ positionals.push(arg);
100
+ }
101
+ return { positionals, values };
102
+ };
103
+ /** `devz check <name>`: runs one of the shared repository gates in the current folder. */
104
+ class CheckCommand {
105
+ static runTestWeight(opts) {
106
+ const parsed = parseRepeatable({ args: opts.args, repeatable: [], single: ['top'] });
107
+ if (typeof parsed === 'string' || parsed.positionals.length === 0) {
108
+ console.error(`devz check test-weight: ${typeof parsed === 'string' ? parsed : 'give at least one JUnit file'}\n${USAGE}`);
109
+ return USAGE_ERROR_CODE;
110
+ }
111
+ const reports = parsed.positionals.map((file) => {
112
+ const path = (0, node_path_1.resolve)(opts.root, file);
113
+ return {
114
+ label: (0, node_path_1.basename)(file).replace(/\.xml$/, ''),
115
+ xml: (0, node_fs_1.existsSync)(path) ? (0, node_fs_1.readFileSync)(path, 'utf8') : '',
116
+ };
117
+ });
118
+ const missing = parsed.positionals.filter((file) => !(0, node_fs_1.existsSync)((0, node_path_1.resolve)(opts.root, file)));
119
+ if (missing.length > 0) {
120
+ console.error(`devz check test-weight: not found: ${missing.join(', ')}`);
121
+ return USAGE_ERROR_CODE;
122
+ }
123
+ const top = parsed.values.top?.[0] === undefined ? undefined : Number(parsed.values.top[0]);
124
+ for (const line of (0, test_weight_1.formatTestWeight)({ report: (0, test_weight_1.analyseJunit)({ reports }), top })) {
125
+ console.log(line);
126
+ }
127
+ return 0;
128
+ }
129
+ static runTestDeadCode(opts) {
130
+ if (opts.args.length > 0) {
131
+ console.error(`devz check test-dead-code: unexpected argument "${opts.args[0]}"\n${USAGE}`);
132
+ return USAGE_ERROR_CODE;
133
+ }
134
+ const candidates = (0, test_dead_code_1.findDeadTestCode)({ root: opts.root });
135
+ for (const kind of ['unreferenced', 'folder-referenced']) {
136
+ const own = candidates.filter((candidate) => candidate.kind === kind);
137
+ console.log(`${kind}: ${own.length}`);
138
+ for (const candidate of own) {
139
+ console.log(` ${candidate.file}`);
140
+ }
141
+ }
142
+ console.log('\nThese are candidates: delete one, run the package suite, and keep it if anything fails or loads it by a built path.');
143
+ return 0;
144
+ }
145
+ static runTestMutate(opts) {
146
+ const [file, ...extra] = opts.args;
147
+ if (file === undefined || extra.length > 0) {
148
+ console.error(`devz check test-mutate: give one ledger file\n${USAGE}`);
149
+ return USAGE_ERROR_CODE;
150
+ }
151
+ const path = (0, node_path_1.resolve)(opts.root, file);
152
+ if (!(0, node_fs_1.existsSync)(path)) {
153
+ console.error(`devz check test-mutate: ledger not found: ${file}`);
154
+ return USAGE_ERROR_CODE;
155
+ }
156
+ const ledger = JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
157
+ if (!Array.isArray(ledger.mutants)) {
158
+ console.error(`devz check test-mutate: ${file} has no "mutants" array`);
159
+ return USAGE_ERROR_CODE;
160
+ }
161
+ const results = (0, test_mutate_1.replayLedger)({ root: opts.root, ledger });
162
+ for (const line of (0, test_mutate_1.formatLedgerResults)(results)) {
163
+ console.log(line);
164
+ }
165
+ return results.every((result) => result.ok) ? 0 : 1;
166
+ }
167
+ static async runTestControls(opts) {
168
+ const [file, ...extra] = opts.args;
169
+ if (file === undefined || extra.length > 0) {
170
+ console.error(`devz check test-controls: give one registry file\n${USAGE}`);
171
+ return USAGE_ERROR_CODE;
172
+ }
173
+ const path = (0, node_path_1.resolve)(opts.root, file);
174
+ if (!(0, node_fs_1.existsSync)(path)) {
175
+ console.error(`devz check test-controls: registry not found: ${file}`);
176
+ return USAGE_ERROR_CODE;
177
+ }
178
+ const registry = JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
179
+ if (!Array.isArray(registry.controls)) {
180
+ console.error(`devz check test-controls: ${file} has no "controls" array`);
181
+ return USAGE_ERROR_CODE;
182
+ }
183
+ const report = await (0, test_controls_1.checkControls)({ root: opts.root, controls: registry.controls });
184
+ if (report.problems.length === 0) {
185
+ console.log(`test-controls: OK - ${report.checked} registered controls found`);
186
+ return 0;
187
+ }
188
+ console.error(`test-controls: ${report.problems.length} of ${report.checked} registered controls are gone:`);
189
+ for (const problem of report.problems) {
190
+ console.error(` [${problem.entry.gate}] ${problem.message}`);
191
+ }
192
+ return 1;
193
+ }
194
+ static runSplitReport(opts) {
195
+ const parsed = parseRepeatable({ args: opts.args, repeatable: [], single: ['dist'] });
196
+ if (typeof parsed === 'string') {
197
+ console.error(`devz check split-report: ${parsed}\n${USAGE}`);
198
+ return USAGE_ERROR_CODE;
199
+ }
200
+ const targets = parsed.positionals.length
201
+ ? parsed.positionals.map((target) => (0, node_path_1.resolve)(opts.root, target))
202
+ : split_report_1.SplitReport.defaultTargets({ root: opts.root });
203
+ for (const packageDir of targets) {
204
+ split_report_1.SplitReport.run({ packageDir, distDir: parsed.values.dist?.[0] });
205
+ }
206
+ return 0;
207
+ }
208
+ static async runCatalog(opts) {
209
+ const parsed = parseRepeatable({
210
+ args: opts.args,
211
+ repeatable: ['glob', 'pinned'],
212
+ single: ['preset-prefix'],
213
+ });
214
+ if (typeof parsed === 'string' || parsed.positionals.length > 0) {
215
+ console.error(`devz check catalog: ${typeof parsed === 'string' ? parsed : `unexpected argument "${parsed.positionals[0]}"`}\n${USAGE}`);
216
+ return USAGE_ERROR_CODE;
217
+ }
218
+ const report = await (0, catalog_1.findCatalogProblems)({
219
+ root: opts.root,
220
+ globs: parsed.values.glob,
221
+ pinned: parsed.values.pinned,
222
+ presetPrefix: parsed.values['preset-prefix']?.[0],
223
+ });
224
+ if (report.noCatalog) {
225
+ console.error('check-catalog: no `workspaces.catalog` in the root package.json');
226
+ return 1;
227
+ }
228
+ if (report.problems.length === 0) {
229
+ console.log(`check-catalog: OK - ${report.entries} entries, ${report.referenced} referenced`);
230
+ return 0;
231
+ }
232
+ console.error(`check-catalog: ${report.problems.length} problem(s):`);
233
+ for (const problem of report.problems) {
234
+ console.error(` ${problem.file}: ${problem.message}`);
235
+ }
236
+ return 1;
237
+ }
238
+ static async run(opts) {
239
+ const { root, args = [] } = opts;
240
+ const [name, ...rest] = args;
241
+ if (name === undefined || name === '--help' || name === '-h') {
242
+ console.log(USAGE);
243
+ return name === undefined ? USAGE_ERROR_CODE : 0;
244
+ }
245
+ if (name === 'module-cycles') {
246
+ return (0, module_cycles_1.runModuleCycles)({
247
+ args: rest,
248
+ print: (line) => console.log(line),
249
+ printError: (line) => console.error(line),
250
+ });
251
+ }
252
+ if (name === 'split-report') {
253
+ return CheckCommand.runSplitReport({ root, args: rest });
254
+ }
255
+ if (name === 'catalog') {
256
+ return CheckCommand.runCatalog({ root, args: rest });
257
+ }
258
+ if (name === 'test-weight') {
259
+ return CheckCommand.runTestWeight({ root, args: rest });
260
+ }
261
+ if (name === 'test-dead-code') {
262
+ return CheckCommand.runTestDeadCode({ root, args: rest });
263
+ }
264
+ if (name === 'test-mutate') {
265
+ return CheckCommand.runTestMutate({ root, args: rest });
266
+ }
267
+ if (name === 'test-controls') {
268
+ return CheckCommand.runTestControls({ root, args: rest });
269
+ }
270
+ if (name !== 'purity') {
271
+ console.error(`devz check: unknown check "${name}"\n${USAGE}`);
272
+ return USAGE_ERROR_CODE;
273
+ }
274
+ if (rest.includes('--help') || rest.includes('-h')) {
275
+ console.log(USAGE);
276
+ return 0;
277
+ }
278
+ const parsed = parsePurityArguments(rest);
279
+ if (typeof parsed === 'string') {
280
+ console.error(`devz check purity: ${parsed}\n${USAGE}`);
281
+ return USAGE_ERROR_CODE;
282
+ }
283
+ if (parsed.claimsFile === undefined) {
284
+ console.error(`devz check purity: --claims <file> is required\n${USAGE}`);
285
+ return USAGE_ERROR_CODE;
286
+ }
287
+ const claimsPath = (0, node_path_1.isAbsolute)(parsed.claimsFile)
288
+ ? parsed.claimsFile
289
+ : (0, node_path_1.resolve)(root, parsed.claimsFile);
290
+ if (!(0, node_fs_1.existsSync)(claimsPath)) {
291
+ console.error(`devz check purity: claims file not found: ${parsed.claimsFile}`);
292
+ return USAGE_ERROR_CODE;
293
+ }
294
+ const claimsModule = await import(claimsPath);
295
+ if (!Array.isArray(claimsModule.PURITY_CLAIMS)) {
296
+ console.error(`devz check purity: ${parsed.claimsFile} does not export PURITY_CLAIMS`);
297
+ return USAGE_ERROR_CODE;
298
+ }
299
+ const lines = [];
300
+ const report = await (0, run_1.checkPurity)({
301
+ root,
302
+ claims: claimsModule.PURITY_CLAIMS,
303
+ options: claimsModule.PURITY_OPTIONS,
304
+ filter: parsed.filter,
305
+ claimsFile: (0, node_path_1.relative)(root, claimsPath),
306
+ print: (line) => (parsed.json ? lines.push(line) : console.log(line)),
307
+ printError: (line) => console.error(line),
308
+ });
309
+ if (report === undefined) {
310
+ return 1;
311
+ }
312
+ if (parsed.json) {
313
+ console.log(JSON.stringify(report, null, 2));
314
+ }
315
+ return report.failed > 0 ? 1 : 0;
316
+ }
317
+ }
318
+ exports.CheckCommand = CheckCommand;
319
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/cli/check.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { basename, isAbsolute, relative, resolve } from 'node:path';\nimport { findCatalogProblems } from '../checks/catalog';\nimport { runModuleCycles } from '../checks/module-cycles';\nimport { checkPurity } from '../checks/purity/run';\nimport { SplitReport } from '../checks/split-report';\nimport { checkControls } from '../checks/test-controls';\nimport type { IControlEntry } from '../checks/test-controls';\nimport { findDeadTestCode } from '../checks/test-dead-code';\nimport { formatLedgerResults, replayLedger } from '../checks/test-mutate';\nimport type { ILedger } from '../checks/test-mutate';\nimport { analyseJunit, formatTestWeight } from '../checks/test-weight';\nimport type { IPurityClaim, IPurityOptions } from '../checks/purity/manifest';\n\nconst USAGE = `Usage: devz check <name> [options]\n\nChecks:\n purity [package] --claims <file> [--json] probe every entry claimed browser-pure (needs a build)\n module-cycles <dir> [--max <n>] list the import cycles of a built ESM folder\n split-report [--dist <dir>] [folder...] report hub files, stray types, missing barrels, long files, cycles (never a gate)\n catalog [--glob <pattern>]... [--pinned <file>]... [--preset-prefix <prefix>]\n guard dependency ranges against the root workspaces.catalog\n test-weight [--top <n>] <junit.xml>... per-file time, cases, assertions, duplicates and skips from bun's JUnit report\n test-dead-code list support files under __tests__ that no other file refers to (candidates)\n test-mutate <ledger.json> replay a mutation ledger: the survivor must go red under each mutant\n test-controls <registry.json> fail when a registered positive-control test is gone\n\npurity\n --claims <file> a module that exports PURITY_CLAIMS (the claims) and, optionally, PURITY_OPTIONS\n [package] probe only the rows of this package; a token that matches nothing fails\n --json print the verdict as JSON (no paths of this machine, no timing) instead of the report\n\n\ntest-weight, test-dead-code, test-mutate, test-controls: the tools of the trim-tests method, see\n .agents/knowledge/process/trimming-tests.md in the IGNIS repository. Produce the JUnit files with\n bun test ./src --parallel --reporter=junit --reporter-outfile=<file>.\n\nmodule-cycles\n <dir>, or --dist <dir> the built folder (dist/esm); exits 1 when more cycles than --max, 2 on a usage error\n\nsplit-report\n --dist <dir> the built ESM folder inside a package: dist/esm, or dist where one build is published (default dist)\n [folder...] package folders to report; none scans every packages/* and examples/* that has a src/\n\ncatalog\n --glob <pattern> a manifest glob beside packages/*/package.json (repeatable)\n --pinned <file> a manifest outside the workspace: its catalogued ranges must equal the catalog's (repeatable)\n --preset-prefix <p> files under this prefix are presets: catalog: is allowed in their peerDependencies\n\n --help print this text and exit`;\n\n/** A usage error: nothing was probed. */\nconst USAGE_ERROR_CODE = 2;\n\ninterface IClaimsModule {\n PURITY_CLAIMS?: IPurityClaim[];\n PURITY_OPTIONS?: IPurityOptions;\n}\n\ninterface IPurityArguments {\n claimsFile?: string;\n filter?: string;\n json: boolean;\n}\n\nconst parsePurityArguments = (args: string[]): IPurityArguments | string => {\n const parsed: IPurityArguments = { json: false };\n\n for (let index = 0; index < args.length; index += 1) {\n const arg = args[index];\n\n if (arg === '--json') {\n parsed.json = true;\n continue;\n }\n\n if (arg === '--claims') {\n const value = args[index + 1];\n if (value === undefined || value.startsWith('--')) {\n return '--claims needs a file';\n }\n\n parsed.claimsFile = value;\n index += 1;\n continue;\n }\n\n if (arg.startsWith('--')) {\n return `unknown option \"${arg}\"`;\n }\n\n if (parsed.filter !== undefined) {\n return `unexpected argument \"${arg}\"`;\n }\n\n parsed.filter = arg;\n }\n\n return parsed;\n};\n\n/** Splits repeatable `--name value` options and positionals; `undefined` for an option that lacks its value. */\nconst parseRepeatable = (opts: {\n args: string[];\n repeatable: string[];\n single: string[];\n}): { positionals: string[]; values: Record<string, string[]> } | string => {\n const { args, repeatable, single } = opts;\n const values: Record<string, string[]> = {};\n const positionals: string[] = [];\n\n for (let index = 0; index < args.length; index += 1) {\n const arg = args[index];\n\n if (arg.startsWith('--')) {\n const name = arg.slice(2);\n if (!repeatable.includes(name) && !single.includes(name)) {\n return `unknown option \"${arg}\"`;\n }\n\n const value = args[index + 1];\n if (value === undefined || value.startsWith('--')) {\n return `${arg} needs a value`;\n }\n\n values[name] = [...(values[name] ?? []), value];\n index += 1;\n continue;\n }\n\n positionals.push(arg);\n }\n\n return { positionals, values };\n};\n\n/** `devz check <name>`: runs one of the shared repository gates in the current folder. */\nexport class CheckCommand {\n private static runTestWeight(opts: { root: string; args: string[] }): number {\n const parsed = parseRepeatable({ args: opts.args, repeatable: [], single: ['top'] });\n if (typeof parsed === 'string' || parsed.positionals.length === 0) {\n console.error(\n `devz check test-weight: ${typeof parsed === 'string' ? parsed : 'give at least one JUnit file'}\\n${USAGE}`,\n );\n return USAGE_ERROR_CODE;\n }\n\n const reports = parsed.positionals.map((file) => {\n const path = resolve(opts.root, file);\n return {\n label: basename(file).replace(/\\.xml$/, ''),\n xml: existsSync(path) ? readFileSync(path, 'utf8') : '',\n };\n });\n const missing = parsed.positionals.filter((file) => !existsSync(resolve(opts.root, file)));\n if (missing.length > 0) {\n console.error(`devz check test-weight: not found: ${missing.join(', ')}`);\n return USAGE_ERROR_CODE;\n }\n\n const top = parsed.values.top?.[0] === undefined ? undefined : Number(parsed.values.top[0]);\n for (const line of formatTestWeight({ report: analyseJunit({ reports }), top })) {\n console.log(line);\n }\n\n return 0;\n }\n\n private static runTestDeadCode(opts: { root: string; args: string[] }): number {\n if (opts.args.length > 0) {\n console.error(`devz check test-dead-code: unexpected argument \"${opts.args[0]}\"\\n${USAGE}`);\n return USAGE_ERROR_CODE;\n }\n\n const candidates = findDeadTestCode({ root: opts.root });\n for (const kind of ['unreferenced', 'folder-referenced'] as const) {\n const own = candidates.filter((candidate) => candidate.kind === kind);\n console.log(`${kind}: ${own.length}`);\n for (const candidate of own) {\n console.log(` ${candidate.file}`);\n }\n }\n console.log(\n '\\nThese are candidates: delete one, run the package suite, and keep it if anything fails or loads it by a built path.',\n );\n\n return 0;\n }\n\n private static runTestMutate(opts: { root: string; args: string[] }): number {\n const [file, ...extra] = opts.args;\n if (file === undefined || extra.length > 0) {\n console.error(`devz check test-mutate: give one ledger file\\n${USAGE}`);\n return USAGE_ERROR_CODE;\n }\n\n const path = resolve(opts.root, file);\n if (!existsSync(path)) {\n console.error(`devz check test-mutate: ledger not found: ${file}`);\n return USAGE_ERROR_CODE;\n }\n\n const ledger: ILedger = JSON.parse(readFileSync(path, 'utf8'));\n if (!Array.isArray(ledger.mutants)) {\n console.error(`devz check test-mutate: ${file} has no \"mutants\" array`);\n return USAGE_ERROR_CODE;\n }\n\n const results = replayLedger({ root: opts.root, ledger });\n for (const line of formatLedgerResults(results)) {\n console.log(line);\n }\n\n return results.every((result) => result.ok) ? 0 : 1;\n }\n\n private static async runTestControls(opts: { root: string; args: string[] }): Promise<number> {\n const [file, ...extra] = opts.args;\n if (file === undefined || extra.length > 0) {\n console.error(`devz check test-controls: give one registry file\\n${USAGE}`);\n return USAGE_ERROR_CODE;\n }\n\n const path = resolve(opts.root, file);\n if (!existsSync(path)) {\n console.error(`devz check test-controls: registry not found: ${file}`);\n return USAGE_ERROR_CODE;\n }\n\n const registry: { controls?: IControlEntry[] } = JSON.parse(readFileSync(path, 'utf8'));\n if (!Array.isArray(registry.controls)) {\n console.error(`devz check test-controls: ${file} has no \"controls\" array`);\n return USAGE_ERROR_CODE;\n }\n\n const report = await checkControls({ root: opts.root, controls: registry.controls });\n if (report.problems.length === 0) {\n console.log(`test-controls: OK - ${report.checked} registered controls found`);\n return 0;\n }\n\n console.error(\n `test-controls: ${report.problems.length} of ${report.checked} registered controls are gone:`,\n );\n for (const problem of report.problems) {\n console.error(` [${problem.entry.gate}] ${problem.message}`);\n }\n\n return 1;\n }\n\n private static runSplitReport(opts: { root: string; args: string[] }): number {\n const parsed = parseRepeatable({ args: opts.args, repeatable: [], single: ['dist'] });\n if (typeof parsed === 'string') {\n console.error(`devz check split-report: ${parsed}\\n${USAGE}`);\n return USAGE_ERROR_CODE;\n }\n\n const targets = parsed.positionals.length\n ? parsed.positionals.map((target) => resolve(opts.root, target))\n : SplitReport.defaultTargets({ root: opts.root });\n\n for (const packageDir of targets) {\n SplitReport.run({ packageDir, distDir: parsed.values.dist?.[0] });\n }\n\n return 0;\n }\n\n private static async runCatalog(opts: { root: string; args: string[] }): Promise<number> {\n const parsed = parseRepeatable({\n args: opts.args,\n repeatable: ['glob', 'pinned'],\n single: ['preset-prefix'],\n });\n if (typeof parsed === 'string' || parsed.positionals.length > 0) {\n console.error(\n `devz check catalog: ${typeof parsed === 'string' ? parsed : `unexpected argument \"${parsed.positionals[0]}\"`}\\n${USAGE}`,\n );\n return USAGE_ERROR_CODE;\n }\n\n const report = await findCatalogProblems({\n root: opts.root,\n globs: parsed.values.glob,\n pinned: parsed.values.pinned,\n presetPrefix: parsed.values['preset-prefix']?.[0],\n });\n\n if (report.noCatalog) {\n console.error('check-catalog: no `workspaces.catalog` in the root package.json');\n return 1;\n }\n\n if (report.problems.length === 0) {\n console.log(`check-catalog: OK - ${report.entries} entries, ${report.referenced} referenced`);\n return 0;\n }\n\n console.error(`check-catalog: ${report.problems.length} problem(s):`);\n for (const problem of report.problems) {\n console.error(` ${problem.file}: ${problem.message}`);\n }\n\n return 1;\n }\n\n static async run(opts: { root: string; args?: string[] }): Promise<number> {\n const { root, args = [] } = opts;\n const [name, ...rest] = args;\n\n if (name === undefined || name === '--help' || name === '-h') {\n console.log(USAGE);\n return name === undefined ? USAGE_ERROR_CODE : 0;\n }\n\n if (name === 'module-cycles') {\n return runModuleCycles({\n args: rest,\n print: (line) => console.log(line),\n printError: (line) => console.error(line),\n });\n }\n\n if (name === 'split-report') {\n return CheckCommand.runSplitReport({ root, args: rest });\n }\n\n if (name === 'catalog') {\n return CheckCommand.runCatalog({ root, args: rest });\n }\n\n if (name === 'test-weight') {\n return CheckCommand.runTestWeight({ root, args: rest });\n }\n\n if (name === 'test-dead-code') {\n return CheckCommand.runTestDeadCode({ root, args: rest });\n }\n\n if (name === 'test-mutate') {\n return CheckCommand.runTestMutate({ root, args: rest });\n }\n\n if (name === 'test-controls') {\n return CheckCommand.runTestControls({ root, args: rest });\n }\n\n if (name !== 'purity') {\n console.error(`devz check: unknown check \"${name}\"\\n${USAGE}`);\n return USAGE_ERROR_CODE;\n }\n\n if (rest.includes('--help') || rest.includes('-h')) {\n console.log(USAGE);\n return 0;\n }\n\n const parsed = parsePurityArguments(rest);\n if (typeof parsed === 'string') {\n console.error(`devz check purity: ${parsed}\\n${USAGE}`);\n return USAGE_ERROR_CODE;\n }\n\n if (parsed.claimsFile === undefined) {\n console.error(`devz check purity: --claims <file> is required\\n${USAGE}`);\n return USAGE_ERROR_CODE;\n }\n\n const claimsPath = isAbsolute(parsed.claimsFile)\n ? parsed.claimsFile\n : resolve(root, parsed.claimsFile);\n if (!existsSync(claimsPath)) {\n console.error(`devz check purity: claims file not found: ${parsed.claimsFile}`);\n return USAGE_ERROR_CODE;\n }\n\n const claimsModule: IClaimsModule = await import(claimsPath);\n if (!Array.isArray(claimsModule.PURITY_CLAIMS)) {\n console.error(`devz check purity: ${parsed.claimsFile} does not export PURITY_CLAIMS`);\n return USAGE_ERROR_CODE;\n }\n\n const lines: string[] = [];\n const report = await checkPurity({\n root,\n claims: claimsModule.PURITY_CLAIMS,\n options: claimsModule.PURITY_OPTIONS,\n filter: parsed.filter,\n claimsFile: relative(root, claimsPath),\n print: (line) => (parsed.json ? lines.push(line) : console.log(line)),\n printError: (line) => console.error(line),\n });\n\n if (report === undefined) {\n return 1;\n }\n\n if (parsed.json) {\n console.log(JSON.stringify(report, null, 2));\n }\n\n return report.failed > 0 ? 1 : 0;\n }\n}\n"]}
@@ -0,0 +1,80 @@
1
+ import type ts from 'typescript';
2
+ type TTypeScript = typeof ts;
3
+ /** What the user may change for one declaration, from the mapping file. */
4
+ export interface IConstClassOverride {
5
+ /** The class name. */
6
+ class?: string;
7
+ /** The type name; only a property of an interface or object type gets a new one. */
8
+ type?: string;
9
+ /** A member key per literal value: `{ "read-only": "READ_ONLY" }`. */
10
+ members?: Record<string, string>;
11
+ /** Leave this declaration alone. */
12
+ skip?: boolean;
13
+ }
14
+ export type TConstClassMap = Record<string, IConstClassOverride>;
15
+ export interface IConstClassProposal {
16
+ /** The key of the mapping file: `path/to/file.ts#TName` or `path/to/file.ts#IContainer.property`. */
17
+ id: string;
18
+ line: number;
19
+ className: string;
20
+ typeName: string;
21
+ /** The literal values and the member key each one gets, in order. */
22
+ members: Array<{
23
+ value: string;
24
+ key: string;
25
+ }>;
26
+ /** What the union also held: `null` or `undefined`. */
27
+ nullish: string[];
28
+ }
29
+ export interface IConstClassUntouched {
30
+ line: number;
31
+ column: number;
32
+ reason: string;
33
+ }
34
+ export interface IConstClassResult {
35
+ output: string;
36
+ changed: boolean;
37
+ proposals: IConstClassProposal[];
38
+ untouched: IConstClassUntouched[];
39
+ }
40
+ /**
41
+ * Turns the string-literal unions of exported declarations into const classes: an exported type
42
+ * alias, and a property of an exported interface or object type. The values stay the same, so every
43
+ * existing literal still type-checks and no call site changes. A union it cannot name is reported.
44
+ */
45
+ export declare class ConstClasses {
46
+ static convert(opts: {
47
+ typescript: TTypeScript;
48
+ fileName: string;
49
+ source: string;
50
+ /** The id prefix of the mapping file: the path of the file relative to the project root. */
51
+ relativePath?: string;
52
+ overrides?: TConstClassMap;
53
+ importFrom?: string;
54
+ }): IConstClassResult;
55
+ private static planMembers;
56
+ /** Every top-level name the file declares or imports. */
57
+ private static declaredNames;
58
+ private static isExported;
59
+ /** The string values of a union, and its `null` or `undefined` members; the union must be string literals (and nullish) only. */
60
+ private static readUnion;
61
+ /** At least two string literals, and nothing else but `null` or `undefined`. */
62
+ private static isEnumerable;
63
+ private static checkPlan;
64
+ private static renderClass;
65
+ /** Reports every string-literal union of an exported declaration the codemod did not handle: it cannot name those. */
66
+ private static findStrays;
67
+ private static isKeySet;
68
+ /** The edit that makes `TConstValue` available: nothing when the file has it, a specifier on an import from the same module, else a new import line. */
69
+ private static planImport;
70
+ }
71
+ /** `devz codemod const-classes [paths...] [--write | --check]`. */
72
+ export declare class ConstClassesCommand {
73
+ static readonly USAGE = "Usage: devz codemod const-classes [paths...] [--write | --check] [--map <file>] [--emit-map <file>] [--import-from <module>] [--include-tests]\n\nTurns a string-literal union in an exported declaration into a const class (SCHEME_SET, isValid) plus\n`TConstValue<typeof X>`, the shape the venizia/const-class-values lint rule asks for. It handles an\nexported type alias and a property of an exported interface or object type. The values stay the same,\nso every existing literal still type-checks and no call site changes.\n\nIt has to choose names: the class is the type name without its leading T, in the plural; a member is\nthe value in upper snake case. The dry run prints every proposal.\n\n --write write the changed files (without it, nothing is written)\n --check dry run that exits 1 while a union can be converted or is left untouched (for CI)\n --map <file> a JSON file of overrides, keyed by the id the dry run prints\n --emit-map <file> write the proposals as a mapping file to edit, then pass it with --map\n --import-from <m> where TConstValue is imported from (default @venizia/ignis-helpers/common)\n --include-tests also convert test files (skipped by default)\n --help print this text and exit\n\nA mapping entry: { \"src/a.ts#TMode\": { \"class\": \"Modes\", \"members\": { \"read-only\": \"READ_ONLY\" } } },\nor { \"skip\": true }. A union in a parameter, a return type, a generic or a nested type is reported as\nleft untouched: it has no name, so write that class by hand. Key sets (Pick, Omit, Extract, Exclude,\nRecord, mapped types) are not enumerable values and are never touched.";
74
+ static run(opts: {
75
+ root: string;
76
+ args: string[];
77
+ }): Promise<number>;
78
+ }
79
+ export {};
80
+ //# sourceMappingURL=const-classes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"const-classes.d.ts","sourceRoot":"","sources":["../../../src/cli/codemod/const-classes.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAGjC,KAAK,WAAW,GAAG,OAAO,EAAE,CAAC;AAU7B,2EAA2E;AAC3E,MAAM,WAAW,mBAAmB;IAClC,sBAAsB;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oCAAoC;IACpC,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAEjE,MAAM,WAAW,mBAAmB;IAClC,qGAAqG;IACrG,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,OAAO,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC/C,uDAAuD;IACvD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,mBAAmB,EAAE,CAAC;IACjC,SAAS,EAAE,oBAAoB,EAAE,CAAC;CACnC;AA4CD;;;;GAIG;AACH,qBAAa,YAAY;IACvB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;QACnB,UAAU,EAAE,WAAW,CAAC;QACxB,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,4FAA4F;QAC5F,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,cAAc,CAAC;QAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,GAAG,iBAAiB;IA4KrB,OAAO,CAAC,MAAM,CAAC,WAAW;IA8C1B,yDAAyD;IACzD,OAAO,CAAC,MAAM,CAAC,aAAa;IAoD5B,OAAO,CAAC,MAAM,CAAC,UAAU;IAQzB,iIAAiI;IACjI,OAAO,CAAC,MAAM,CAAC,SAAS;IA+BxB,gFAAgF;IAChF,OAAO,CAAC,MAAM,CAAC,YAAY;IAuB3B,OAAO,CAAC,MAAM,CAAC,SAAS;IAqCxB,OAAO,CAAC,MAAM,CAAC,WAAW;IAuB1B,sHAAsH;IACtH,OAAO,CAAC,MAAM,CAAC,UAAU;IAkCzB,OAAO,CAAC,MAAM,CAAC,QAAQ;IA2BvB,wJAAwJ;IACxJ,OAAO,CAAC,MAAM,CAAC,UAAU;CAqD1B;AAyDD,mEAAmE;AACnE,qBAAa,mBAAmB;IAC9B,MAAM,CAAC,QAAQ,CAAC,KAAK,ipDAAS;WAEjB,GAAG,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC;CA2K1E"}