@conformetry/cli 0.0.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 +21 -0
- package/README.md +1301 -0
- package/dist/main.module-BIsov6qb.js +709 -0
- package/dist/src/index.d.ts +662 -0
- package/dist/src/index.js +2 -0
- package/dist/src/main.d.ts +1 -0
- package/dist/src/main.js +20 -0
- package/package.json +78 -0
|
@@ -0,0 +1,662 @@
|
|
|
1
|
+
import { CommandRunner } from 'nest-commander';
|
|
2
|
+
import { ConfigurationService } from '@conformetry/configuration';
|
|
3
|
+
import { ConsoleLogger } from '@nestjs/common';
|
|
4
|
+
import { GenerationService } from '@conformetry/generation';
|
|
5
|
+
import { InventoryService } from '@conformetry/output';
|
|
6
|
+
import pino from 'pino';
|
|
7
|
+
import { ReportingService } from '@conformetry/output';
|
|
8
|
+
import { ValidationService } from '@conformetry/validation';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Renders a conformetry template from the configured registry.
|
|
12
|
+
*
|
|
13
|
+
* Unknown options are allowed through deliberately: a template's parameters
|
|
14
|
+
* are not known until the template is chosen, so they cannot be declared as
|
|
15
|
+
* flags ahead of time. They are matched against the template's own schema
|
|
16
|
+
* instead. This is also why the template is selected with `--template` rather
|
|
17
|
+
* than `--name` — nearly every template takes a `name` parameter, and
|
|
18
|
+
* reserving that flag made it impossible to supply.
|
|
19
|
+
*
|
|
20
|
+
* `--template` is optional at the parse layer so a bare `generate` reaches
|
|
21
|
+
* this command and can offer a picker instead of failing at argument parsing
|
|
22
|
+
* with a name the reader would have had to look up elsewhere.
|
|
23
|
+
*/
|
|
24
|
+
export declare class GenerateCommand extends CommandRunner {
|
|
25
|
+
private readonly configurationService;
|
|
26
|
+
private readonly generationService;
|
|
27
|
+
private readonly logger;
|
|
28
|
+
constructor(configurationService: ConfigurationService, generationService: GenerationService, logger: LoggerService);
|
|
29
|
+
/** Resolves the template's inputs and writes its files. */
|
|
30
|
+
private generate;
|
|
31
|
+
/**
|
|
32
|
+
* Logs a command line the input service refused, and fails the run.
|
|
33
|
+
*
|
|
34
|
+
* A required input that could not be asked for is the reader's own typing
|
|
35
|
+
* to fix, so it is reported as a rejected command line rather than as a
|
|
36
|
+
* crash. Nothing was generated, and the next move is to pass the flag it
|
|
37
|
+
* named, not to read a stack trace.
|
|
38
|
+
*/
|
|
39
|
+
private rejectCommandLine;
|
|
40
|
+
/**
|
|
41
|
+
* Refuses the flag `--template` replaced, rather than ignoring it.
|
|
42
|
+
*
|
|
43
|
+
* Unknown options are allowed through so a template's own inputs can be
|
|
44
|
+
* passed as flags, which means commander does not reject `--generator` for
|
|
45
|
+
* this command the way it would for any other. Without this it would be
|
|
46
|
+
* read as an input nothing declares and quietly discarded, leaving a stale
|
|
47
|
+
* script to prompt or to fail for the wrong reason.
|
|
48
|
+
*/
|
|
49
|
+
private rejectRemovedGeneratorOption;
|
|
50
|
+
/**
|
|
51
|
+
* Settles which template to render: the one named, the one picked, or none.
|
|
52
|
+
*
|
|
53
|
+
* The missing case and the unknown case are decided together here rather
|
|
54
|
+
* than split between argument parsing and the command body, which is what
|
|
55
|
+
* lets the picker sit between them. Whether anybody can be asked is read
|
|
56
|
+
* from the one predicate that knows — a non-terminal stdin is what once let
|
|
57
|
+
* a prompt hang a CI job until it timed out.
|
|
58
|
+
*/
|
|
59
|
+
private resolveTemplateName;
|
|
60
|
+
/** Parses the optional configuration path. */
|
|
61
|
+
parseConfig(value: string | undefined): string | undefined;
|
|
62
|
+
/** Parses the output directory override. */
|
|
63
|
+
parseDirectory(value: string | undefined): string | undefined;
|
|
64
|
+
/**
|
|
65
|
+
* Parses the name of the template to render.
|
|
66
|
+
*
|
|
67
|
+
* Optional, so a bare `generate` is not rejected at argument parsing with a
|
|
68
|
+
* name the reader has not been shown yet.
|
|
69
|
+
*/
|
|
70
|
+
parseTemplate(value: string | undefined): string | undefined;
|
|
71
|
+
/**
|
|
72
|
+
* Renders the template, reporting a refused command line as one.
|
|
73
|
+
*
|
|
74
|
+
* Only an `InputError` is caught: a required input nobody could be asked
|
|
75
|
+
* for is a flag the caller has to pass, where anything else is a genuine
|
|
76
|
+
* failure and keeps its stack.
|
|
77
|
+
*/
|
|
78
|
+
run(passedParameters: string[], options: GenerateCommandOptions): Promise<void>;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Options accepted by the generate command.
|
|
83
|
+
*
|
|
84
|
+
* Keys are the camel-cased long flag names, which is how commander reports
|
|
85
|
+
* parsed options — `--directory` arrives as `directory`.
|
|
86
|
+
*/
|
|
87
|
+
export declare interface GenerateCommandOptions {
|
|
88
|
+
config?: string;
|
|
89
|
+
directory?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Optional at the parse layer only. A bare `generate` has to reach the
|
|
92
|
+
* command body so the missing case and the unknown case can be decided
|
|
93
|
+
* together; the template itself is still required, and is either supplied
|
|
94
|
+
* here or chosen at the picker.
|
|
95
|
+
*/
|
|
96
|
+
template?: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Provides the generate command.
|
|
101
|
+
*/
|
|
102
|
+
export declare class GenerateModule {
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Lists every instance the configured globs find, and which templates explain
|
|
107
|
+
* each one.
|
|
108
|
+
*
|
|
109
|
+
* Each path printed is usable as the `--instances` argument to the templates
|
|
110
|
+
* command, so the two read as one pair: this side answers "what is generated
|
|
111
|
+
* code here", the other answers "what standard does this path answer to".
|
|
112
|
+
*
|
|
113
|
+
* Output goes to standard output rather than through the logger, which asserts
|
|
114
|
+
* every message opens with an emoji and a verb — right for a log line, wrong
|
|
115
|
+
* for a listing.
|
|
116
|
+
*/
|
|
117
|
+
export declare class InstancesCommand extends CommandRunner {
|
|
118
|
+
private readonly configurationService;
|
|
119
|
+
private readonly inventoryService;
|
|
120
|
+
private readonly logger;
|
|
121
|
+
constructor(configurationService: ConfigurationService, inventoryService: InventoryService, logger: LoggerService);
|
|
122
|
+
/** Parses the optional configuration path. */
|
|
123
|
+
parseConfig(value: string | undefined): string | undefined;
|
|
124
|
+
/** Selects the machine-readable listing. */
|
|
125
|
+
parseJson(): boolean;
|
|
126
|
+
/** Parses the optional template filter. */
|
|
127
|
+
parseTemplates(value: string | undefined): string[] | undefined;
|
|
128
|
+
/** Writes every instance found, filtered to the given templates. */
|
|
129
|
+
run(_passedParameters: string[], options: InstancesCommandOptions): Promise<void>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** Options accepted by the instances command. */
|
|
133
|
+
export declare interface InstancesCommandOptions {
|
|
134
|
+
config?: string;
|
|
135
|
+
json?: boolean;
|
|
136
|
+
templates?: string[];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Provides the instances command.
|
|
141
|
+
*/
|
|
142
|
+
export declare class InstancesModule {
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Structured values that belong beside a log line rather than inside it.
|
|
147
|
+
*
|
|
148
|
+
* Counts, percentages, and durations are the values that change on every
|
|
149
|
+
* occurrence, so they are carried as fields: the message stays constant and
|
|
150
|
+
* groupable in telemetry, and the numbers stay queryable instead of having to
|
|
151
|
+
* be parsed back out of prose.
|
|
152
|
+
*
|
|
153
|
+
* The named members are the recurring ones; the index signature keeps the
|
|
154
|
+
* argument open for whatever a given call site needs to attach.
|
|
155
|
+
*/
|
|
156
|
+
declare interface LogData {
|
|
157
|
+
[key: string]: unknown;
|
|
158
|
+
/** How many things the operation handled. */
|
|
159
|
+
count?: number;
|
|
160
|
+
/** Wall-clock milliseconds the operation took. */
|
|
161
|
+
durationMs?: number;
|
|
162
|
+
/** Completion between 0 and 100. */
|
|
163
|
+
percent?: number;
|
|
164
|
+
/** How many things the operation set out to handle. */
|
|
165
|
+
total?: number;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Transient-scoped logger so each injecting class gets its own instance.
|
|
170
|
+
* Each consumer calls `setContext(ClassName.name)` to tag every log line
|
|
171
|
+
* with the originating class. Backed by pino for structured JSON output in
|
|
172
|
+
* production and human-readable pretty-print in development.
|
|
173
|
+
*
|
|
174
|
+
* Messages follow one grammar: an emoji naming the subject, a verb in present
|
|
175
|
+
* progressive or past tense, then the object. Values that vary per call —
|
|
176
|
+
* counts, percentages, durations — go in the `data` argument rather than the
|
|
177
|
+
* message, so the message stays constant enough for telemetry to group on.
|
|
178
|
+
*
|
|
179
|
+
* ```ts
|
|
180
|
+
* this.logger.info("📥 Downloading CSEL sources", undefined, { total: 428 });
|
|
181
|
+
* this.logger.info("📥 Downloaded CSEL sources", undefined, { count: 412 });
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
declare @Injectable({ scope: Scope.TRANSIENT })
|
|
185
|
+
class LoggerService extends ConsoleLogger {
|
|
186
|
+
// 🏗 Dependency Injection
|
|
187
|
+
|
|
188
|
+
constructor() {
|
|
189
|
+
super();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// 🔐 Private Fields
|
|
193
|
+
|
|
194
|
+
private static readonly isProduction =
|
|
195
|
+
process.env["NODE_ENV"] === "production";
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Built on first use, not when this file is evaluated.
|
|
199
|
+
*
|
|
200
|
+
* A destination fixed at import time could only ever be chosen by this
|
|
201
|
+
* package, since every consumer's own code runs after its imports.
|
|
202
|
+
*/
|
|
203
|
+
private static rootLogger: pino.Logger | undefined;
|
|
204
|
+
|
|
205
|
+
/** Whether lines go to standard error instead of standard output. */
|
|
206
|
+
private static writesToStandardError = false;
|
|
207
|
+
|
|
208
|
+
private child: pino.Logger = LoggerService.root;
|
|
209
|
+
|
|
210
|
+
// 🔑 Public Fields
|
|
211
|
+
|
|
212
|
+
// 🔏 Private Methods
|
|
213
|
+
|
|
214
|
+
/** Build the pino instance for production or local development output. */
|
|
215
|
+
private static createRootLogger(): pino.Logger {
|
|
216
|
+
const level = process.env["LOG_LEVEL"] ?? "info";
|
|
217
|
+
|
|
218
|
+
if (LoggerService.isProduction) {
|
|
219
|
+
return LoggerService.writesToStandardError
|
|
220
|
+
? pino({ level }, pino.destination(STANDARD_ERROR_DESCRIPTOR))
|
|
221
|
+
: pino({ level });
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return pino({
|
|
225
|
+
level,
|
|
226
|
+
transport: {
|
|
227
|
+
options: {
|
|
228
|
+
colorize: true,
|
|
229
|
+
destination: LoggerService.writesToStandardError
|
|
230
|
+
? STANDARD_ERROR_DESCRIPTOR
|
|
231
|
+
: STANDARD_OUTPUT_DESCRIPTOR,
|
|
232
|
+
// The emoji is a field, not part of the message, so the console can
|
|
233
|
+
// show it while telemetry stores unadorned prose. `ignore` then keeps
|
|
234
|
+
// it from being printed a second time in the trailing object.
|
|
235
|
+
ignore: "pid,hostname,emoji",
|
|
236
|
+
messageFormat: "{emoji} {msg}",
|
|
237
|
+
singleLine: true,
|
|
238
|
+
},
|
|
239
|
+
target: "pino-pretty",
|
|
240
|
+
},
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Sends every subsequent line to standard error instead of standard output.
|
|
246
|
+
*
|
|
247
|
+
* For a command-line application whose standard output *is* its result. A log
|
|
248
|
+
* line sharing that stream is not a diagnostic beside the data, it is a
|
|
249
|
+
* corruption of it. Call it before anything logs — the first statement of the
|
|
250
|
+
* application's bootstrap.
|
|
251
|
+
*
|
|
252
|
+
* A call after the first line warns and changes nothing: the destination is
|
|
253
|
+
* fixed when the pino instance is built, and tearing down a transport
|
|
254
|
+
* somebody is writing through would be worse than refusing. The warning is
|
|
255
|
+
* the point — silently leaving the lines on standard output is how a caller
|
|
256
|
+
* would ship a corrupted pipe without ever being told.
|
|
257
|
+
*/
|
|
258
|
+
static logToStandardError(): void {
|
|
259
|
+
if (LoggerService.rootLogger !== undefined) {
|
|
260
|
+
process.emitWarning(
|
|
261
|
+
"LoggerService.logToStandardError() was called after the first log line, so log lines still go to standard output and anything piping that stream will read them as data. Call it as the first statement of the application's bootstrap.",
|
|
262
|
+
);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
LoggerService.writesToStandardError = true;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Fails a malformed message in development, and never in production.
|
|
271
|
+
*
|
|
272
|
+
* A logger that throws in production turns an observability call into an
|
|
273
|
+
* outage, so the check runs only where a developer is present to fix it.
|
|
274
|
+
*/
|
|
275
|
+
private assertConventionalMessage(args: {
|
|
276
|
+
context: string | undefined;
|
|
277
|
+
parsed: ParsedLogMessage;
|
|
278
|
+
}): void {
|
|
279
|
+
if (
|
|
280
|
+
LoggerService.isProduction ||
|
|
281
|
+
this.shouldSkipConventionalMessageValidation(args.context)
|
|
282
|
+
) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const violation = this.getConventionalMessageViolation(args.parsed);
|
|
287
|
+
|
|
288
|
+
if (violation !== undefined) {
|
|
289
|
+
throw new Error(violation);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Assembles the object pino merges into the line. */
|
|
294
|
+
private buildBindings(args: {
|
|
295
|
+
context: string | undefined;
|
|
296
|
+
data: LogData | undefined;
|
|
297
|
+
parsed: ParsedLogMessage;
|
|
298
|
+
}): Record<string, unknown> {
|
|
299
|
+
this.assertConventionalMessage({
|
|
300
|
+
context: args.context,
|
|
301
|
+
parsed: args.parsed,
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
...args.data,
|
|
306
|
+
context: args.context,
|
|
307
|
+
// Telemetry gets prose; only the console-bound transport reads this.
|
|
308
|
+
...(LoggerService.isProduction ? {} : { emoji: args.parsed.emoji }),
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/** Returns a human-readable explanation when the message format is invalid. */
|
|
313
|
+
private getConventionalMessageViolation(
|
|
314
|
+
parsed: ParsedLogMessage,
|
|
315
|
+
): string | undefined {
|
|
316
|
+
const emoji = parsed.emoji;
|
|
317
|
+
const text = parsed.text;
|
|
318
|
+
|
|
319
|
+
if (emoji === undefined) {
|
|
320
|
+
return `Log message must start with an emoji naming its subject, then a verb: "${text}"`;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const firstWord = FIRST_WORD_PATTERN.exec(text)?.[1];
|
|
324
|
+
|
|
325
|
+
if (firstWord === undefined || !this.isConventionalVerb(firstWord)) {
|
|
326
|
+
return `Log message must begin with a verb in present progressive or past tense, got "${firstWord ?? ""}": "${emoji} ${text}"`;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Whether a word is a verb in one of the two tenses the convention allows.
|
|
334
|
+
*
|
|
335
|
+
* Present progressive means the operation is under way; past means it
|
|
336
|
+
* finished. Regular morphology covers both, so a new verb needs no
|
|
337
|
+
* registration anywhere — only irregular pasts are enumerated.
|
|
338
|
+
*/
|
|
339
|
+
private isConventionalVerb(word: string): boolean {
|
|
340
|
+
const lowercased = word.toLowerCase();
|
|
341
|
+
|
|
342
|
+
return (
|
|
343
|
+
lowercased.endsWith("ing") ||
|
|
344
|
+
lowercased.endsWith("ed") ||
|
|
345
|
+
IRREGULAR_PAST_VERBS.has(lowercased)
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/** Splits a leading emoji off a message, leaving prose behind. */
|
|
350
|
+
private parseMessage(message: unknown): ParsedLogMessage {
|
|
351
|
+
const text = String(message);
|
|
352
|
+
const match = LEADING_EMOJI_PATTERN.exec(text);
|
|
353
|
+
const emoji = match?.[1];
|
|
354
|
+
|
|
355
|
+
return emoji === undefined
|
|
356
|
+
? { emoji: undefined, text }
|
|
357
|
+
: { emoji, text: text.slice(match?.[0].length) };
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Whether a context is intentionally exempt from the validation rule. */
|
|
361
|
+
private shouldSkipConventionalMessageValidation(
|
|
362
|
+
context: string | undefined,
|
|
363
|
+
): boolean {
|
|
364
|
+
return context !== undefined && UNVALIDATED_LOG_CONTEXTS.has(context);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// 🌎 Public Methods
|
|
368
|
+
|
|
369
|
+
/** The pino instance every logger's child is taken from. */
|
|
370
|
+
private static get root(): pino.Logger {
|
|
371
|
+
LoggerService.rootLogger ??= LoggerService.createRootLogger();
|
|
372
|
+
|
|
373
|
+
return LoggerService.rootLogger;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** Normalizes unknown errors into a stable message and timestamped log line. */
|
|
377
|
+
buildErrorLogEntry(
|
|
378
|
+
context: string,
|
|
379
|
+
error: unknown,
|
|
380
|
+
): { errorMessage: string; logLine: string } {
|
|
381
|
+
const errorMessage =
|
|
382
|
+
error instanceof Error ? error.stack || error.message : String(error);
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
errorMessage,
|
|
386
|
+
logLine: `[${new Date().toISOString()}] ${context}: ${errorMessage}\n`,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** Builds a timestamped output log file path and ensures the output directory exists. */
|
|
391
|
+
createTimestampedOutputLogFilePath(filePrefix: string): string {
|
|
392
|
+
const outputDirectory = path.join(process.cwd(), "output");
|
|
393
|
+
if (!existsSync(outputDirectory)) {
|
|
394
|
+
mkdirSync(outputDirectory, { recursive: true });
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return path.join(
|
|
398
|
+
outputDirectory,
|
|
399
|
+
`${filePrefix}-${new Date().toISOString().replaceAll(/[:.]/g, "-")}.log`,
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/** Logs a debug message at the `debug` level. */
|
|
404
|
+
override debug(message: unknown, context?: string, data?: LogData): void {
|
|
405
|
+
const parsed = this.parseMessage(message);
|
|
406
|
+
this.child.debug(
|
|
407
|
+
this.buildBindings({ context: context ?? this.context, data, parsed }),
|
|
408
|
+
parsed.text,
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Logs an error message at the `error` level, optionally including a stack trace.
|
|
414
|
+
*
|
|
415
|
+
* `ConsoleLogger.error` spends a third slot on a context string that the
|
|
416
|
+
* other levels do not have, so this one accepts either: a string keeps
|
|
417
|
+
* NestJS's meaning, an object is structured data like everywhere else.
|
|
418
|
+
*/
|
|
419
|
+
override error(
|
|
420
|
+
message: unknown,
|
|
421
|
+
stackOrContext?: string,
|
|
422
|
+
contextOrData?: LogData | string,
|
|
423
|
+
): void {
|
|
424
|
+
const parsed = this.parseMessage(message);
|
|
425
|
+
const data = typeof contextOrData === "object" ? contextOrData : undefined;
|
|
426
|
+
const context =
|
|
427
|
+
typeof contextOrData === "string" ? contextOrData : this.context;
|
|
428
|
+
|
|
429
|
+
this.child.error(
|
|
430
|
+
{
|
|
431
|
+
...this.buildBindings({ context, data, parsed }),
|
|
432
|
+
stack: stackOrContext,
|
|
433
|
+
},
|
|
434
|
+
parsed.text,
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/** Logs an informational message at the `info` level. */
|
|
439
|
+
info(message: unknown, context?: string, data?: LogData): void {
|
|
440
|
+
const parsed = this.parseMessage(message);
|
|
441
|
+
this.child.info(
|
|
442
|
+
this.buildBindings({ context: context ?? this.context, data, parsed }),
|
|
443
|
+
parsed.text,
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Logs an informational message at the `info` level.
|
|
449
|
+
*
|
|
450
|
+
* NestJS and `nest-commander` call this method directly as part of the
|
|
451
|
+
* framework's own `LoggerService` contract, so it must keep working
|
|
452
|
+
* exactly as before. Application code should call `info` instead — the
|
|
453
|
+
* same behavior under a name that says what level it logs at.
|
|
454
|
+
*/
|
|
455
|
+
override log(message: unknown, context?: string, data?: LogData): void {
|
|
456
|
+
this.info(message, context, data);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/** Sets the context label included in every subsequent log line. */
|
|
460
|
+
override setContext(context: string): void {
|
|
461
|
+
super.setContext(context);
|
|
462
|
+
this.child = LoggerService.root.child({ context });
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/** Logs a verbose message at the `trace` level. */
|
|
466
|
+
override verbose(message: unknown, context?: string, data?: LogData): void {
|
|
467
|
+
const parsed = this.parseMessage(message);
|
|
468
|
+
this.child.trace(
|
|
469
|
+
this.buildBindings({ context: context ?? this.context, data, parsed }),
|
|
470
|
+
parsed.text,
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/** Logs a warning message at the `warn` level. */
|
|
475
|
+
override warn(message: unknown, context?: string, data?: LogData): void {
|
|
476
|
+
const parsed = this.parseMessage(message);
|
|
477
|
+
this.child.warn(
|
|
478
|
+
this.buildBindings({ context: context ?? this.context, data, parsed }),
|
|
479
|
+
parsed.text,
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/**
|
|
485
|
+
* Root NestJS application module.
|
|
486
|
+
*/
|
|
487
|
+
export declare class MainModule {
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
/** A message split into the emoji the console shows and the prose telemetry stores. */
|
|
491
|
+
declare interface ParsedLogMessage {
|
|
492
|
+
emoji: string | undefined;
|
|
493
|
+
text: string;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Names every template the configuration declares, and which instances each
|
|
498
|
+
* explains.
|
|
499
|
+
*
|
|
500
|
+
* With `--instances` it answers the other direction — which templates explain a
|
|
501
|
+
* given path. That is one command rather than two because a path can belong to
|
|
502
|
+
* several templates at once: nothing records where an instance came from, so
|
|
503
|
+
* attribution is inferred from file overlap and ties are real.
|
|
504
|
+
*
|
|
505
|
+
* Output goes to standard output rather than through the logger, which asserts
|
|
506
|
+
* every message opens with an emoji and a verb — right for a log line, wrong
|
|
507
|
+
* for a listing.
|
|
508
|
+
*/
|
|
509
|
+
export declare class TemplatesCommand extends CommandRunner {
|
|
510
|
+
private readonly configurationService;
|
|
511
|
+
private readonly inventoryService;
|
|
512
|
+
private readonly logger;
|
|
513
|
+
constructor(configurationService: ConfigurationService, inventoryService: InventoryService, logger: LoggerService);
|
|
514
|
+
/** Parses the optional configuration path. */
|
|
515
|
+
parseConfig(value: string | undefined): string | undefined;
|
|
516
|
+
/** Parses the optional instance filter. */
|
|
517
|
+
parseInstances(value: string | undefined): string[] | undefined;
|
|
518
|
+
/** Selects the machine-readable listing. */
|
|
519
|
+
parseJson(): boolean;
|
|
520
|
+
/** Writes every declared template, filtered to the given instances. */
|
|
521
|
+
run(_passedParameters: string[], options: TemplatesCommandOptions): Promise<void>;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/** Options accepted by the templates command. */
|
|
525
|
+
export declare interface TemplatesCommandOptions {
|
|
526
|
+
config?: string;
|
|
527
|
+
instances?: string[];
|
|
528
|
+
json?: boolean;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Provides the templates command.
|
|
533
|
+
*/
|
|
534
|
+
export declare class TemplatesModule {
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Validates instances against their conformetry templates.
|
|
539
|
+
*
|
|
540
|
+
* Which paths are instances comes from the configuration's `instances` globs,
|
|
541
|
+
* which `--instances` overrides for a one-off run, and `--templates` narrows
|
|
542
|
+
* the other half of the pairing. Neither flag overrides the other: each
|
|
543
|
+
* removes candidates from one side before templates and instances are paired,
|
|
544
|
+
* so a run is their intersection.
|
|
545
|
+
*
|
|
546
|
+
* `--templates` is the one option that is asked for when absent, because it
|
|
547
|
+
* is the one whose absence is genuinely a question rather than a default. It
|
|
548
|
+
* is asked only where somebody can answer: with no terminal the run falls
|
|
549
|
+
* back to every template, which is exactly what every invocation predating
|
|
550
|
+
* this flag already did.
|
|
551
|
+
*/
|
|
552
|
+
export declare class ValidateCommand extends CommandRunner {
|
|
553
|
+
private readonly configurationService;
|
|
554
|
+
private readonly reportingService;
|
|
555
|
+
private readonly validationService;
|
|
556
|
+
private readonly logger;
|
|
557
|
+
constructor(configurationService: ConfigurationService, reportingService: ReportingService, validationService: ValidationService, logger: LoggerService);
|
|
558
|
+
/**
|
|
559
|
+
* Describes why the run failed, naming the instances that fell short.
|
|
560
|
+
*
|
|
561
|
+
* A file count alone stopped being the whole story once thresholds existed:
|
|
562
|
+
* findings can be printed for an instance that still passed, so the message
|
|
563
|
+
* has to say which instances actually failed and by how much.
|
|
564
|
+
*/
|
|
565
|
+
private describeFailure;
|
|
566
|
+
/**
|
|
567
|
+
* Expands every configured glob group into instances, or the single
|
|
568
|
+
* override group `--instances` supplies.
|
|
569
|
+
*
|
|
570
|
+
* Groups exist so that substitutions can differ per glob: `type` is
|
|
571
|
+
* `packages` for one set of paths and `applications` for another, and no
|
|
572
|
+
* generic rule can tell them apart.
|
|
573
|
+
*/
|
|
574
|
+
private findInstances;
|
|
575
|
+
/**
|
|
576
|
+
* Offers a picker, or declines to ask where nobody could answer.
|
|
577
|
+
*
|
|
578
|
+
* Declining resolves to no selection rather than to a refusal, which is
|
|
579
|
+
* where this parts company with `generate`: a template that cannot be
|
|
580
|
+
* chosen leaves that command with nothing to render, where this one still
|
|
581
|
+
* has a whole workspace to validate.
|
|
582
|
+
*
|
|
583
|
+
* The loaded configuration is handed over as-is — a definition already
|
|
584
|
+
* carries the name and description a choice needs, so mapping it here would
|
|
585
|
+
* be a second source the picker could disagree with.
|
|
586
|
+
*/
|
|
587
|
+
private promptForTemplateNames;
|
|
588
|
+
/**
|
|
589
|
+
* Reports a narrowing that matched nothing, as itself.
|
|
590
|
+
*
|
|
591
|
+
* A run with no instances produces no findings, and a report rendering no
|
|
592
|
+
* findings is indistinguishable from a clean one — which would turn naming
|
|
593
|
+
* a real template that happens to have nothing under it into a green
|
|
594
|
+
* result. The reader is told nothing matched instead.
|
|
595
|
+
*/
|
|
596
|
+
private reportEmptySelection;
|
|
597
|
+
/**
|
|
598
|
+
* Writes the report and fails the run when anything fell short.
|
|
599
|
+
*
|
|
600
|
+
* The report is the command's product, not a log line: it is a multi-line
|
|
601
|
+
* document written for a reader, and routing it through the logger would
|
|
602
|
+
* both bury it in log framing and force prose no telemetry can group on.
|
|
603
|
+
*/
|
|
604
|
+
private reportResult;
|
|
605
|
+
/**
|
|
606
|
+
* Pairs the two filters into the instances a run covers.
|
|
607
|
+
*
|
|
608
|
+
* With both flags supplied the globbed paths are intersected with the
|
|
609
|
+
* selected templates' own instances, rather than one flag winning: each
|
|
610
|
+
* simply removes candidates from one side.
|
|
611
|
+
*/
|
|
612
|
+
private selectInstances;
|
|
613
|
+
/**
|
|
614
|
+
* Narrows the run to the selected templates, or leaves it whole.
|
|
615
|
+
*
|
|
616
|
+
* `undefined` means no narrowing, which is a different thing from an empty
|
|
617
|
+
* selection: it is what an absent flag, the `all` sentinel, and a cancelled
|
|
618
|
+
* picker all mean, and it reproduces the run this command made before the
|
|
619
|
+
* flag existed.
|
|
620
|
+
*/
|
|
621
|
+
private selectTemplates;
|
|
622
|
+
/** Parses the optional configuration path. */
|
|
623
|
+
parseConfig(value: string | undefined): string | undefined;
|
|
624
|
+
/** Parses the optional instance glob override. */
|
|
625
|
+
parseInstances(value: string | undefined): string[] | undefined;
|
|
626
|
+
/** Parses the optional language filter. */
|
|
627
|
+
parseLanguages(value: string | undefined): string[] | undefined;
|
|
628
|
+
/**
|
|
629
|
+
* Parses the optional template filter.
|
|
630
|
+
*
|
|
631
|
+
* Comma-delimited like its `--instances` and `--languages` siblings, so all
|
|
632
|
+
* three read the same way on a command line.
|
|
633
|
+
*/
|
|
634
|
+
parseTemplates(value: string | undefined): string[] | undefined;
|
|
635
|
+
/** Parses the optional run-level conformance threshold. */
|
|
636
|
+
parseThreshold(value: string | undefined): number | undefined;
|
|
637
|
+
/** Runs validation and reports every difference found. */
|
|
638
|
+
run(_passedParameters: string[], options: ValidateCommandOptions): Promise<void>;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
/** Options accepted by the validate command. */
|
|
642
|
+
export declare interface ValidateCommandOptions {
|
|
643
|
+
config?: string;
|
|
644
|
+
instances?: string[];
|
|
645
|
+
languages?: string[];
|
|
646
|
+
/**
|
|
647
|
+
* Template names to narrow the run to, or `["all"]` for every one.
|
|
648
|
+
*
|
|
649
|
+
* Absent means the caller has not decided, which is what the picker asks
|
|
650
|
+
* about; `["all"]` means they have, and nothing is asked.
|
|
651
|
+
*/
|
|
652
|
+
templates?: string[];
|
|
653
|
+
threshold?: number;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Provides the validate command.
|
|
658
|
+
*/
|
|
659
|
+
export declare class ValidateModule {
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
export { }
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as e, c as t, i as n, l as r, n as i, o as a, r as o, s, t as c } from "../main.module-BIsov6qb.js";
|
|
2
|
+
export { r as GenerateCommand, t as GenerateModule, s as InstancesCommand, a as InstancesModule, c as MainModule, e as TemplatesCommand, n as TemplatesModule, o as ValidateCommand, i as ValidateModule };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/src/main.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { t as e, u as t } from "../main.module-BIsov6qb.js";
|
|
3
|
+
import { CommandFactory as n } from "nest-commander";
|
|
4
|
+
import "reflect-metadata";
|
|
5
|
+
//#region packages/ic-suite/conformetry/conformetry-cli/src/main.ts
|
|
6
|
+
async function r() {
|
|
7
|
+
let r = new t();
|
|
8
|
+
r.setContext("CommandFactory"), await n.run(e, {
|
|
9
|
+
bufferLogs: !0,
|
|
10
|
+
errorHandler: (e) => {
|
|
11
|
+
process.exitCode = 1, r.error(e);
|
|
12
|
+
},
|
|
13
|
+
logger: r,
|
|
14
|
+
serviceErrorHandler: (e) => {
|
|
15
|
+
process.exitCode = 1, r.error(e);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
r();
|
|
20
|
+
//#endregion
|