@conformetry/configuration 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 +919 -0
- package/dist/src/index.d.ts +1034 -0
- package/dist/src/index.js +988 -0
- package/package.json +70 -0
|
@@ -0,0 +1,1034 @@
|
|
|
1
|
+
import { InventoriedInstance } from '@conformetry/core';
|
|
2
|
+
import { InventoriedPairing } from '@conformetry/core';
|
|
3
|
+
import { InventoriedTemplate } from '@conformetry/core';
|
|
4
|
+
import { PreparedValidationDocument } from '@conformetry/core';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* How a caller says "every template" to `validate --templates`.
|
|
8
|
+
*
|
|
9
|
+
* A sentinel rather than a second flag, because supplying a value is how this
|
|
10
|
+
* command line opts out of being prompted: omitting `--templates` in a
|
|
11
|
+
* terminal asks, and passing `all` decides without narrowing. It lives beside
|
|
12
|
+
* the other command-line vocabulary because that is what it is — the word a
|
|
13
|
+
* caller types — and `assertNoCollisions` reads it from here so a template can
|
|
14
|
+
* never be named the same thing.
|
|
15
|
+
*/
|
|
16
|
+
export declare const ALL_TEMPLATES_SELECTION = "all";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Provides the configuration layer's one public service.
|
|
20
|
+
*
|
|
21
|
+
* The five modules above supply the collaborators `ConfigurationService` is
|
|
22
|
+
* assembled from, and none of them is re-exported: a consumer outside this
|
|
23
|
+
* package injects `ConfigurationService` and nothing else, which is what makes
|
|
24
|
+
* this layer one entry point rather than a bag of services a caller has to
|
|
25
|
+
* know the names of. The three sibling ic-suite toolchains publish exactly
|
|
26
|
+
* this pair, and conformetry now reads as the same shape.
|
|
27
|
+
*/
|
|
28
|
+
export declare class ConfigurationModule {
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The one answer to "what is this run actually configured to do".
|
|
33
|
+
*
|
|
34
|
+
* Every question a caller outside this package can ask about configuration is
|
|
35
|
+
* asked here: what a config file declares, which templates it points at, which
|
|
36
|
+
* instances exist and what explains them, what a placeholder renders to, and
|
|
37
|
+
* what to ask a person for when an input was left off. A consumer therefore
|
|
38
|
+
* injects this and nothing else from this package, which is what makes the
|
|
39
|
+
* configuration layer one layer rather than a bag of collaborators a caller
|
|
40
|
+
* has to know the names of — the shape callidescope, codependix and codometer
|
|
41
|
+
* already publish.
|
|
42
|
+
*
|
|
43
|
+
* Loading a config file is the one job held here rather than delegated;
|
|
44
|
+
* everything below it is a one-line hand-off. Prompting, option parsing,
|
|
45
|
+
* rendering, template discovery, instance discovery and instance-group reading
|
|
46
|
+
* stay six classes in their own files, because they are six different jobs.
|
|
47
|
+
* What they stop being is six public entry points.
|
|
48
|
+
*/
|
|
49
|
+
export declare class ConfigurationService {
|
|
50
|
+
private readonly inputPromptingService;
|
|
51
|
+
private readonly inputService;
|
|
52
|
+
private readonly instanceDiscoveryService;
|
|
53
|
+
private readonly instanceGroupService;
|
|
54
|
+
private readonly renderingService;
|
|
55
|
+
private readonly templateDiscoveryService;
|
|
56
|
+
constructor(inputPromptingService: InputPromptingService, inputService: InputService, instanceDiscoveryService: InstanceDiscoveryService, instanceGroupService: InstanceGroupService, renderingService: RenderingService, templateDiscoveryService: TemplateDiscoveryService);
|
|
57
|
+
/**
|
|
58
|
+
* Fills in the optional halves of one parsed generator entry.
|
|
59
|
+
*
|
|
60
|
+
* A generator with no inputs and no instances is legal — it renders a fixed
|
|
61
|
+
* template nobody validates — so both default to empty rather than failing.
|
|
62
|
+
*/
|
|
63
|
+
private applyGeneratorDefaults;
|
|
64
|
+
/**
|
|
65
|
+
* Walks upward from the process cwd looking for the workspace manifest.
|
|
66
|
+
*
|
|
67
|
+
* Used to resolve a config path given relative to the workspace root even
|
|
68
|
+
* when the command was invoked from a nested directory.
|
|
69
|
+
*/
|
|
70
|
+
private findWorkspaceRoot;
|
|
71
|
+
/** Loads a config module, choosing the reader by extension. */
|
|
72
|
+
private loadConfigurationModule;
|
|
73
|
+
/** Reads a JSON or JSONC config file. */
|
|
74
|
+
private loadJsonConfiguration;
|
|
75
|
+
/**
|
|
76
|
+
* Resolves a config path against the cwd, falling back to the workspace root.
|
|
77
|
+
*/
|
|
78
|
+
private resolveConfigurationPath;
|
|
79
|
+
/** Derives the case variants every template can reference from one name. */
|
|
80
|
+
buildNameSubstitutions(name: string): Substitutions;
|
|
81
|
+
/** Reads one template folder. */
|
|
82
|
+
collectTemplate(args: Parameters<TemplateDiscoveryService["collectTemplate"]>[0]): TemplateDefinition;
|
|
83
|
+
/** Reads every configured generator's template folder. */
|
|
84
|
+
collectTemplates(args: {
|
|
85
|
+
configuration: ConformetryConfiguration;
|
|
86
|
+
workingDirectory: string;
|
|
87
|
+
}): TemplateDefinition[];
|
|
88
|
+
/** Expands instance globs into the instances that exist. */
|
|
89
|
+
findInstances(args: FindInstancesArguments): Instance[];
|
|
90
|
+
/** Whether anybody is there to answer a question. */
|
|
91
|
+
isAtTerminal(): boolean;
|
|
92
|
+
/** Whether a group locates its instances inside the hosts its tags select. */
|
|
93
|
+
isProjectScoped(group: ConformetryInstanceGroup): boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Loads, validates, and normalizes a conformetry configuration file.
|
|
96
|
+
*
|
|
97
|
+
* Throws `UnknownConfigurationFileTypeError` for an unreadable extension, and
|
|
98
|
+
* propagates the Zod error for a malformed registry — a bad config should
|
|
99
|
+
* fail loudly rather than silently validate nothing.
|
|
100
|
+
*/
|
|
101
|
+
loadConformetryConfiguration(configurationPath: string): Promise<ConformetryConfiguration>;
|
|
102
|
+
/** Resolves every instance to the template, or templates, that explain it. */
|
|
103
|
+
matchInstances(args: {
|
|
104
|
+
instances: Instance[];
|
|
105
|
+
templates: TemplateDefinition[];
|
|
106
|
+
}): ResolvedInstances;
|
|
107
|
+
/** Splits a comma-delimited filter option into its values. */
|
|
108
|
+
parseCommaDelimitedOption(value: string | undefined): string[] | undefined;
|
|
109
|
+
/** Trims an optional string option, treating blank as absent. */
|
|
110
|
+
parseOptionalOption(value: string | undefined): string | undefined;
|
|
111
|
+
/** Parses a threshold option as a ratio from 0 to 1. */
|
|
112
|
+
parseThresholdOption(value: string | undefined): number | undefined;
|
|
113
|
+
/**
|
|
114
|
+
* Prepares the rendered template and instance document pairs for each matched
|
|
115
|
+
* instance, restricted to the extensions the caller's languages claim.
|
|
116
|
+
*/
|
|
117
|
+
prepareDocuments(args: PrepareDocumentsArguments): PreparedInstanceDocuments[];
|
|
118
|
+
/** Asks which single template to run, filtering as the caller types. */
|
|
119
|
+
promptForTemplate(templates: Parameters<InputPromptingService["promptForTemplate"]>[0]): Promise<string | undefined>;
|
|
120
|
+
/** Asks which templates to narrow a run to. */
|
|
121
|
+
promptForTemplates(templates: Parameters<InputPromptingService["promptForTemplates"]>[0]): Promise<string[] | undefined>;
|
|
122
|
+
/** Keeps the groups a host with no project graph can actually locate. */
|
|
123
|
+
readWorkspaceGroups(groups: readonly ConformetryInstanceGroup[]): ConformetryInstanceGroup[];
|
|
124
|
+
/** Renders template contents with mustache. */
|
|
125
|
+
renderContent(args: Parameters<RenderingService["renderContent"]>[0]): string;
|
|
126
|
+
/** Renders a template path with mustache. */
|
|
127
|
+
renderPath(args: Parameters<RenderingService["renderPath"]>[0]): string;
|
|
128
|
+
/** Resolves generator inputs from raw command-line arguments. */
|
|
129
|
+
resolveGeneratorInputs(args: ResolveGeneratorInputsArguments): Promise<Record<string, string>>;
|
|
130
|
+
/** Lists every file a matched instance's template requires it to have. */
|
|
131
|
+
resolveInstanceFiles(instances: MatchedInstance[]): InstanceFile[];
|
|
132
|
+
/** Lists every instance found, paired with the templates that explain it. */
|
|
133
|
+
resolveInventoriedInstances(args: ResolveInventoryArguments): ReturnType<InstanceDiscoveryService["resolveInventoriedInstances"]>;
|
|
134
|
+
/** Lists every template declared, paired with the instances it explains. */
|
|
135
|
+
resolveInventoriedTemplates(args: ResolveInventoryArguments): ReturnType<InstanceDiscoveryService["resolveInventoriedTemplates"]>;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The loaded configuration: one entry per generator.
|
|
140
|
+
*
|
|
141
|
+
* An array rather than a keyed record because a generator's name is already a
|
|
142
|
+
* field, and a record made the name true in two places at once.
|
|
143
|
+
*/
|
|
144
|
+
export declare type ConformetryConfiguration = ConformetryGeneratorDefinition[];
|
|
145
|
+
|
|
146
|
+
/** One generator, with the template it renders and the instances it governs. */
|
|
147
|
+
export declare interface ConformetryGeneratorDefinition {
|
|
148
|
+
description?: string;
|
|
149
|
+
/**
|
|
150
|
+
* The values this generator substitutes, as JSON Schema fragments. Named for
|
|
151
|
+
* what the implementation calls them everywhere else — a generator takes
|
|
152
|
+
* inputs and renders a template with them.
|
|
153
|
+
*/
|
|
154
|
+
inputs: Record<string, ConformetryGeneratorInputDefinition>;
|
|
155
|
+
/**
|
|
156
|
+
* Where this generator's output already lives in the workspace. Validation
|
|
157
|
+
* expands these to find what to check; generation reads them to learn where
|
|
158
|
+
* a new instance belongs.
|
|
159
|
+
*/
|
|
160
|
+
instances: ConformetryInstanceGroup[];
|
|
161
|
+
name: string;
|
|
162
|
+
/** The template folder, relative to the workspace root. */
|
|
163
|
+
templatePath: string;
|
|
164
|
+
/**
|
|
165
|
+
* Lowest conformance score, from 0 to 1, an instance of this template may
|
|
166
|
+
* have and still pass.
|
|
167
|
+
*
|
|
168
|
+
* Left undefined when unset rather than defaulted to 1, so a run-level
|
|
169
|
+
* `--threshold` still has somewhere to apply. The default is reached only
|
|
170
|
+
* when no level supplies a value.
|
|
171
|
+
*/
|
|
172
|
+
threshold?: number;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/** One configurable input, expressed as a JSON Schema fragment. */
|
|
176
|
+
export declare type ConformetryGeneratorInputDefinition = Record<string, unknown>;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* One set of instance globs and the substitutions their template renders with.
|
|
180
|
+
*
|
|
181
|
+
* Directory and file patterns behave differently on purpose — see
|
|
182
|
+
* `Instance.path`.
|
|
183
|
+
*/
|
|
184
|
+
export declare interface ConformetryInstanceGroup {
|
|
185
|
+
/**
|
|
186
|
+
* Globs locating this group's instances.
|
|
187
|
+
*
|
|
188
|
+
* Workspace-relative on their own. A host that resolves `tags` may instead
|
|
189
|
+
* read them relative to each labelled host it selects — see
|
|
190
|
+
* `ConformetryNxInstanceGroup` in `@conformetry/nx` — which is why a group
|
|
191
|
+
* naming only tags is legal: it selects without locating.
|
|
192
|
+
*/
|
|
193
|
+
patterns?: string[] | undefined;
|
|
194
|
+
/**
|
|
195
|
+
* Values every placeholder this generator's template uses must be given.
|
|
196
|
+
* Mustache renders an unknown placeholder as empty, so a missing entry shows
|
|
197
|
+
* up as a silent hole rather than an error.
|
|
198
|
+
*/
|
|
199
|
+
substitutions?: Record<string, string> | undefined;
|
|
200
|
+
/**
|
|
201
|
+
* Labels selecting the hosts this group applies to.
|
|
202
|
+
*
|
|
203
|
+
* The base configuration carries them uninterpreted, because it has no
|
|
204
|
+
* notion of a host to match them against; `conformetry-nx` reads them as Nx
|
|
205
|
+
* project tags, and another host is free to read them as something else. A
|
|
206
|
+
* group with no tags applies everywhere.
|
|
207
|
+
*/
|
|
208
|
+
tags?: string[] | undefined;
|
|
209
|
+
/**
|
|
210
|
+
* Lowest conformance score the instances in this group may have, overriding
|
|
211
|
+
* the generator's own threshold.
|
|
212
|
+
*
|
|
213
|
+
* This is what makes a migration bearable: one directory being brought onto
|
|
214
|
+
* a new template can be held to a lower bar while every other instance of
|
|
215
|
+
* the same template stays strict.
|
|
216
|
+
*/
|
|
217
|
+
threshold?: number | undefined;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Arguments for expanding instance globs into instances. */
|
|
221
|
+
export declare interface FindInstancesArguments {
|
|
222
|
+
/** Glob patterns, resolved against the working directory. */
|
|
223
|
+
readonly patterns: string[];
|
|
224
|
+
/**
|
|
225
|
+
* Applied to every instance these patterns produce. Callers that need
|
|
226
|
+
* per-project values, such as an Nx plugin supplying `type`, call once per
|
|
227
|
+
* project rather than passing a lookup table.
|
|
228
|
+
*/
|
|
229
|
+
readonly substitutions?: Substitutions;
|
|
230
|
+
/** Applied to every instance these patterns produce, when the group sets one. */
|
|
231
|
+
readonly threshold?: number;
|
|
232
|
+
readonly workingDirectory: string;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Thrown when a command line cannot be turned into a run.
|
|
237
|
+
*
|
|
238
|
+
* One class rather than one per cause: every cause is the same event to
|
|
239
|
+
* whoever catches it — nothing was generated, and the fix is to retype the
|
|
240
|
+
* flags. Only the wording varies, which is what the factory below does.
|
|
241
|
+
*/
|
|
242
|
+
export declare class InputError extends Error {
|
|
243
|
+
constructor(message: string);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Parses command-line options into generator inputs.
|
|
248
|
+
*
|
|
249
|
+
* Generator inputs are not declared as CLI flags ahead of time — they come
|
|
250
|
+
* from whichever generator the user selected — so they are scanned out of the
|
|
251
|
+
* raw argument list and matched against the generator's schema.
|
|
252
|
+
*/
|
|
253
|
+
declare class InputOptionsService {
|
|
254
|
+
constructor();
|
|
255
|
+
/**
|
|
256
|
+
* Reads one option into the accumulating input map and reports how many
|
|
257
|
+
* argument tokens it consumed, so the caller can advance past its value.
|
|
258
|
+
*/
|
|
259
|
+
private collectOneInput;
|
|
260
|
+
/** Reads the option name from `--flag` or `--flag=value`. */
|
|
261
|
+
private readOptionName;
|
|
262
|
+
/**
|
|
263
|
+
* Reads an option's value from either `--flag=value` or `--flag value`.
|
|
264
|
+
*
|
|
265
|
+
* Returns `undefined` when the next token is another flag, so a valueless
|
|
266
|
+
* flag does not swallow the option that follows it.
|
|
267
|
+
*/
|
|
268
|
+
private readOptionValue;
|
|
269
|
+
/** Matches an option name to a schema property, in camel or kebab form. */
|
|
270
|
+
private resolvePropertyName;
|
|
271
|
+
/**
|
|
272
|
+
* Scans raw arguments for flags matching the generator's schema.
|
|
273
|
+
*
|
|
274
|
+
* Unknown flags are ignored rather than rejected: the argument list also
|
|
275
|
+
* carries the command's own options, which are handled elsewhere.
|
|
276
|
+
*/
|
|
277
|
+
collectGeneratorInputs(args: {
|
|
278
|
+
rawArguments: string[];
|
|
279
|
+
schema: JsonSchemaDefinition;
|
|
280
|
+
}): Record<string, string>;
|
|
281
|
+
/** Coerces mixed-typed runtime options into string inputs. */
|
|
282
|
+
normalizeRuntimeOptions(options: Record<string, unknown>): Record<string, string | undefined>;
|
|
283
|
+
/**
|
|
284
|
+
* Resolves where generated files should land: an explicit output option, a
|
|
285
|
+
* resolved project root, or a directory named after the generator.
|
|
286
|
+
*/
|
|
287
|
+
resolveTargetDirectoryPath(args: {
|
|
288
|
+
defaultGeneratedOutputDirectory?: string;
|
|
289
|
+
generatorName: string;
|
|
290
|
+
options: Record<string, unknown>;
|
|
291
|
+
resolveProjectRootPath?: (projectName: string) => string | undefined;
|
|
292
|
+
}): string;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Asks the user for input values interactively.
|
|
297
|
+
*
|
|
298
|
+
* Whether anybody *can* be asked is decided here rather than by the caller,
|
|
299
|
+
* and `promptForInput` refuses rather than draws itself when nobody can. That
|
|
300
|
+
* used to be the caller's job, behind a `--no-interactive` flag; a flag is a
|
|
301
|
+
* poor place for it, because forgetting to consult it is what hung this CLI
|
|
302
|
+
* in non-interactive environments.
|
|
303
|
+
*
|
|
304
|
+
* `isAtTerminal` stays public because some callers genuinely need the question
|
|
305
|
+
* rather than the refusal: an **optional** input nobody can be asked about is
|
|
306
|
+
* left out where a required one is refused, and the two template pickers below
|
|
307
|
+
* are only ever reached once a command has already asked it.
|
|
308
|
+
*
|
|
309
|
+
* Those pickers widen this service's remit from "values a schema declares" to
|
|
310
|
+
* "also, which template to run". That is a deliberate trade: one prompting
|
|
311
|
+
* seam is easier to keep honest than two, and a second one would have to read
|
|
312
|
+
* the terminal for itself.
|
|
313
|
+
*/
|
|
314
|
+
declare class InputPromptingService {
|
|
315
|
+
private readonly inputSchemaService;
|
|
316
|
+
constructor(inputSchemaService: InputSchemaService);
|
|
317
|
+
/** Overridable so tests never touch a real terminal. */
|
|
318
|
+
private readonly promptRunner;
|
|
319
|
+
/**
|
|
320
|
+
* Refuses to draw a prompt nobody can answer.
|
|
321
|
+
*
|
|
322
|
+
* `prompts` does not fail on a non-terminal stdin — it renders the menu,
|
|
323
|
+
* never resolves, and lets the process exit 0, so a run that generated
|
|
324
|
+
* nothing reads as one that succeeded.
|
|
325
|
+
*/
|
|
326
|
+
private assertCanPrompt;
|
|
327
|
+
/** Renders one template as a choice, omitting an absent description. */
|
|
328
|
+
private describeChoice;
|
|
329
|
+
/**
|
|
330
|
+
* Whether anybody is there to answer a question.
|
|
331
|
+
*
|
|
332
|
+
* The one place the terminal is read, so the refusal in front of a prompt
|
|
333
|
+
* and the caller's decision to skip an optional input cannot disagree about
|
|
334
|
+
* what counts as interactive. `isTTY` is read as falsy rather than coerced:
|
|
335
|
+
* `@types/node` calls it a `boolean` while it is `undefined` off a terminal,
|
|
336
|
+
* so lint rejects the coercion that would say so.
|
|
337
|
+
*/
|
|
338
|
+
isAtTerminal(): boolean;
|
|
339
|
+
/**
|
|
340
|
+
* Prompts for one value, offering a choice list when the schema declares an
|
|
341
|
+
* `enum` and free text otherwise.
|
|
342
|
+
*/
|
|
343
|
+
promptForInput(input: SchemaInput): Promise<string | undefined>;
|
|
344
|
+
/**
|
|
345
|
+
* Asks which single template to run, filtering as the caller types.
|
|
346
|
+
*
|
|
347
|
+
* Autocomplete rather than a plain select because a template name is long
|
|
348
|
+
* by design — `nestjs-service-module` — and typing a fragment is what makes
|
|
349
|
+
* that cheap. Whether anybody can be asked at all is the caller's decision,
|
|
350
|
+
* already taken by the time this is reached.
|
|
351
|
+
*/
|
|
352
|
+
promptForTemplate(templates: readonly TemplateChoice[]): Promise<string | undefined>;
|
|
353
|
+
/**
|
|
354
|
+
* Asks which templates to narrow a run to, offering the `all` sentinel
|
|
355
|
+
* alongside them so the picker can express everything the flag can.
|
|
356
|
+
*
|
|
357
|
+
* Ticking nothing resolves to no selection rather than an empty one: an
|
|
358
|
+
* empty narrowing would validate nothing at all, where the caller plainly
|
|
359
|
+
* declined to narrow.
|
|
360
|
+
*/
|
|
361
|
+
promptForTemplates(templates: readonly TemplateChoice[]): Promise<string[] | undefined>;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Reads and enforces the JSON Schema fragments that describe generator inputs.
|
|
366
|
+
*
|
|
367
|
+
* Every accessor tolerates a malformed or absent schema: a generator with no
|
|
368
|
+
* declared parameters should still run, and an author's typo should not crash
|
|
369
|
+
* the CLI before it can report anything useful.
|
|
370
|
+
*/
|
|
371
|
+
declare class InputSchemaService {
|
|
372
|
+
constructor();
|
|
373
|
+
/** Reads one property off a schema fragment when it is an object. */
|
|
374
|
+
private readSchemaProperty;
|
|
375
|
+
/** Validates a value against a schema `enum`, when one is declared. */
|
|
376
|
+
private validateEnum;
|
|
377
|
+
/** Validates a value against `minLength` and `maxLength`. */
|
|
378
|
+
private validateLength;
|
|
379
|
+
/** Validates a value against a schema `pattern`. */
|
|
380
|
+
private validatePattern;
|
|
381
|
+
/** Describes one input, resolving its schema fragment and required flag. */
|
|
382
|
+
describeInput(args: {
|
|
383
|
+
inputName: string;
|
|
384
|
+
schema: JsonSchemaDefinition;
|
|
385
|
+
}): SchemaInput;
|
|
386
|
+
/** Reads the string members of a schema `enum`. */
|
|
387
|
+
readEnumValues(propertySchema: unknown): string[];
|
|
388
|
+
/** Reads a schema `description`, falling back to a generic prompt. */
|
|
389
|
+
readPromptMessage(input: SchemaInput): string;
|
|
390
|
+
/** Lists the input names a schema declares. */
|
|
391
|
+
readPropertyNames(schema: JsonSchemaDefinition): string[];
|
|
392
|
+
/**
|
|
393
|
+
* Validates a value, returning `true` or the reason it failed.
|
|
394
|
+
*
|
|
395
|
+
* An empty value is only an error when the input is required, so optional
|
|
396
|
+
* inputs can be skipped by pressing enter at a prompt.
|
|
397
|
+
*/
|
|
398
|
+
validateValue(args: {
|
|
399
|
+
input: SchemaInput;
|
|
400
|
+
value: unknown;
|
|
401
|
+
}): string | true;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Resolves generator inputs from the command line, asking for what is missing.
|
|
406
|
+
*
|
|
407
|
+
* Nobody passes a "may I prompt" boolean any more. It used to be threaded
|
|
408
|
+
* through every method here from a `--no-interactive` flag, which put the one
|
|
409
|
+
* decision that can hang the process in the hands of each caller in turn;
|
|
410
|
+
* `InputPromptingService` owns it now and refuses at the prompt itself.
|
|
411
|
+
*/
|
|
412
|
+
declare class InputService {
|
|
413
|
+
private readonly inputOptionsService;
|
|
414
|
+
private readonly inputPromptingService;
|
|
415
|
+
private readonly inputSchemaService;
|
|
416
|
+
constructor(inputOptionsService: InputOptionsService, inputPromptingService: InputPromptingService, inputSchemaService: InputSchemaService);
|
|
417
|
+
/** Validates a value the caller already had, throwing if it is invalid. */
|
|
418
|
+
private acceptProvidedValue;
|
|
419
|
+
/** Walks a schema, taking each value from the resolver or a prompt. */
|
|
420
|
+
private resolveInputs;
|
|
421
|
+
/**
|
|
422
|
+
* Obtains one missing value by prompting.
|
|
423
|
+
*
|
|
424
|
+
* With nobody at a terminal the two kinds of input part ways: an optional
|
|
425
|
+
* one is left out, and a required one is refused. Prompting anyway is what
|
|
426
|
+
* used to hang the process, and refusing an optional input would fail runs
|
|
427
|
+
* that never needed the value at all.
|
|
428
|
+
*/
|
|
429
|
+
private resolveMissingValue;
|
|
430
|
+
/** Splits a comma-delimited filter option into its values. */
|
|
431
|
+
parseCommaDelimitedOption(value: string | undefined): string[] | undefined;
|
|
432
|
+
/** Trims an optional string option, treating blank as absent. */
|
|
433
|
+
parseOptionalOption(value: string | undefined): string | undefined;
|
|
434
|
+
/** Trims a required string option, rejecting blank values. */
|
|
435
|
+
parseRequiredOption(args: {
|
|
436
|
+
optionName: string;
|
|
437
|
+
value: string;
|
|
438
|
+
}): string;
|
|
439
|
+
/**
|
|
440
|
+
* Parses a threshold option as a ratio from 0 to 1.
|
|
441
|
+
*
|
|
442
|
+
* Rejected loudly rather than clamped: `--threshold 90` is someone meaning
|
|
443
|
+
* 90%, and silently reading it as "always passes" would turn a typo into a
|
|
444
|
+
* validation run that can never fail.
|
|
445
|
+
*/
|
|
446
|
+
parseThresholdOption(value: string | undefined): number | undefined;
|
|
447
|
+
/** Resolves generator inputs from raw command-line arguments. */
|
|
448
|
+
resolveGeneratorInputs(args: ResolveGeneratorInputsArguments): Promise<Record<string, string>>;
|
|
449
|
+
/** Resolves inputs from values the caller already parsed. */
|
|
450
|
+
resolveInputsFromValues(args: ResolveInputsFromValuesArguments): Promise<Record<string, string>>;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* A place a template's tree is rendered into, plus the name it renders with.
|
|
455
|
+
*
|
|
456
|
+
* Instances come from the caller's glob expansion. Nothing here knows how they
|
|
457
|
+
* were found — that is the host's job, because globbing a workspace and
|
|
458
|
+
* reading project labels is workspace knowledge, not template knowledge.
|
|
459
|
+
*/
|
|
460
|
+
export declare interface Instance {
|
|
461
|
+
/**
|
|
462
|
+
* Absolute paths the match is restricted to, or `undefined` for the whole
|
|
463
|
+
* directory tree.
|
|
464
|
+
*
|
|
465
|
+
* This is what lets a two-file template like `nestjs-service-file` win. A
|
|
466
|
+
* directory glob leaves the scope open and the largest fitting template
|
|
467
|
+
* wins; a file glob narrows the scope to the matched files, so a template
|
|
468
|
+
* describing exactly those files fits better than one describing the whole
|
|
469
|
+
* directory.
|
|
470
|
+
*/
|
|
471
|
+
readonly fileScope?: string[];
|
|
472
|
+
/** Drives the name substitutions — a directory basename or a filename stem. */
|
|
473
|
+
readonly nameStem: string;
|
|
474
|
+
/**
|
|
475
|
+
* Absolute path to the directory the template's tree is laid over.
|
|
476
|
+
*
|
|
477
|
+
* A template that produces a folder contains that folder, so this is the
|
|
478
|
+
* folder's *parent*: `nestjs-service-module` holds `{{nameKebabCase}}/…`,
|
|
479
|
+
* and its path is `…/src/modules`. A template that produces loose files
|
|
480
|
+
* holds them at its root, and its path is the directory those files sit in.
|
|
481
|
+
*/
|
|
482
|
+
readonly path: string;
|
|
483
|
+
/**
|
|
484
|
+
* Substitutions the caller supplies on top of the derived name variants,
|
|
485
|
+
* such as `type` from the project graph. Mustache renders an unknown
|
|
486
|
+
* placeholder as empty, so anything a template references must arrive here.
|
|
487
|
+
*/
|
|
488
|
+
readonly substitutions?: Substitutions;
|
|
489
|
+
/**
|
|
490
|
+
* Lowest conformance score this instance may have, from the instance group
|
|
491
|
+
* that located it. The narrowest of the three levels, so it wins.
|
|
492
|
+
*/
|
|
493
|
+
readonly threshold?: number;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Expands instance glob patterns into instances.
|
|
498
|
+
*
|
|
499
|
+
* The globs are the author's assertion of what was generated from a template;
|
|
500
|
+
* nothing is inferred from directory names or marker files, which is what the
|
|
501
|
+
* previous matcher did and what made it repo-specific.
|
|
502
|
+
*/
|
|
503
|
+
declare class InstanceDiscoveryLocatingService {
|
|
504
|
+
constructor();
|
|
505
|
+
/**
|
|
506
|
+
* Derives the substitutions an instance's own location answers.
|
|
507
|
+
*
|
|
508
|
+
* `type` is the top-level directory the instance sits in — `packages` for
|
|
509
|
+
* `packages/widgets` — because that is what a project template's own
|
|
510
|
+
* `project.json` renders into its paths. Derived rather than configured for
|
|
511
|
+
* the same reason the name variants are: it is a fact about where the
|
|
512
|
+
* instance is, and restating it per glob is how the two drift apart. A
|
|
513
|
+
* configured value still wins, which is what a workspace nesting its
|
|
514
|
+
* projects deeper needs.
|
|
515
|
+
*/
|
|
516
|
+
private deriveLocationSubstitutions;
|
|
517
|
+
/**
|
|
518
|
+
* Returns the literal filename suffix a pattern ends with, such as
|
|
519
|
+
* `.service.ts` for `**\/*.service.ts`, or `""` when the pattern's last
|
|
520
|
+
* segment holds no wildcard.
|
|
521
|
+
*
|
|
522
|
+
* This is what a file instance's name is derived from: the part of the
|
|
523
|
+
* filename the glob did not spell out is the name.
|
|
524
|
+
*/
|
|
525
|
+
private resolveGlobSuffix;
|
|
526
|
+
/**
|
|
527
|
+
* Derives the name an instance's substitutions are built from.
|
|
528
|
+
*
|
|
529
|
+
* A directory is named by itself. A file is named by what remains once the
|
|
530
|
+
* glob's literal suffix is removed, so `differences.service.ts` matched by
|
|
531
|
+
* `*.service.ts` and `differences.service.unit.test.ts` matched by
|
|
532
|
+
* `*.service.unit.test.ts` both yield `differences` — which is what collapses the
|
|
533
|
+
* two into a single two-file instance.
|
|
534
|
+
*/
|
|
535
|
+
private resolveNameStem;
|
|
536
|
+
/**
|
|
537
|
+
* Expands every pattern and returns one instance per distinct path, name,
|
|
538
|
+
* and scope kind.
|
|
539
|
+
*
|
|
540
|
+
* A directory match leaves the file scope open, so the largest fitting
|
|
541
|
+
* template wins. File matches for the same name are unioned into one scoped
|
|
542
|
+
* instance, so a template describing exactly those files wins instead. A
|
|
543
|
+
* directory and a file instance for the same name stay separate on purpose:
|
|
544
|
+
* both run, and deduplication reports the finding from the smaller template.
|
|
545
|
+
*/
|
|
546
|
+
findInstances(args: FindInstancesArguments): Instance[];
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Decides which template an instance directory was generated from.
|
|
551
|
+
*
|
|
552
|
+
* Nothing declares the answer — an instance is ordinary code with no marker
|
|
553
|
+
* saying where it came from — so the template is inferred from how much of its
|
|
554
|
+
* structure the instance already has.
|
|
555
|
+
*/
|
|
556
|
+
declare class InstanceDiscoveryMatchingService {
|
|
557
|
+
private readonly templateDiscoveryService;
|
|
558
|
+
private readonly renderingService;
|
|
559
|
+
constructor(templateDiscoveryService: TemplateDiscoveryService, renderingService: RenderingService);
|
|
560
|
+
/**
|
|
561
|
+
* Orders template matches best-first.
|
|
562
|
+
*
|
|
563
|
+
* Coverage ratio leads, because absolute count alone lets a large template
|
|
564
|
+
* win on a weak partial match — a module directory matching three of a
|
|
565
|
+
* seven-file GraphQL template would beat nothing, and used to. Absolute
|
|
566
|
+
* count breaks ratio ties, so a five-file module template beats a two-file
|
|
567
|
+
* service-file template when both match completely. Name makes it
|
|
568
|
+
* deterministic.
|
|
569
|
+
*/
|
|
570
|
+
private compareMatches;
|
|
571
|
+
/**
|
|
572
|
+
* Builds the substitutions an instance's template is rendered with.
|
|
573
|
+
*
|
|
574
|
+
* Name variants come from the instance's stem; caller-supplied values are
|
|
575
|
+
* spread last so an explicit `type` or `description` always wins.
|
|
576
|
+
*/
|
|
577
|
+
buildSubstitutions(instance: Instance): Substitutions;
|
|
578
|
+
/**
|
|
579
|
+
* Resolves every instance to the template — or templates — that explain it.
|
|
580
|
+
*
|
|
581
|
+
* An instance matching nothing, or matching two templates equally well but
|
|
582
|
+
* only partially, is returned as unmatched rather than dropped: the caller
|
|
583
|
+
* asserted these are instances, so silence would hide both a drifted
|
|
584
|
+
* instance and a pair of indistinguishable templates.
|
|
585
|
+
*
|
|
586
|
+
* A complete tie is different, and is matched against every tied template. A
|
|
587
|
+
* module holding both a command and a service really is an instance of both
|
|
588
|
+
* `nestjs-command-module` and `nestjs-service-module`; calling that ambiguous
|
|
589
|
+
* would demand the author narrow a glob that is not wrong.
|
|
590
|
+
*/
|
|
591
|
+
matchInstances(args: {
|
|
592
|
+
instances: Instance[];
|
|
593
|
+
templates: TemplateDefinition[];
|
|
594
|
+
}): ResolvedInstances;
|
|
595
|
+
/**
|
|
596
|
+
* Weighs every template that shares at least one file with the instance,
|
|
597
|
+
* best-first.
|
|
598
|
+
*
|
|
599
|
+
* Public because a caller may need the ranking itself and not just the
|
|
600
|
+
* verdict: nothing records which template an instance came from, so an
|
|
601
|
+
* ambiguous or unmatched outcome is only explainable by showing what was
|
|
602
|
+
* weighed and how well each template fitted.
|
|
603
|
+
*/
|
|
604
|
+
matchTemplates(args: {
|
|
605
|
+
instance: Instance;
|
|
606
|
+
substitutions: Substitutions;
|
|
607
|
+
templates: TemplateDefinition[];
|
|
608
|
+
}): TemplateMatch[];
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Finds the generated code a workspace holds, and decides what explains it.
|
|
613
|
+
*
|
|
614
|
+
* This is the workspace-facing half of discovery: the template-discovery module
|
|
615
|
+
* reads the templates root, and this one reads the tree those templates were
|
|
616
|
+
* rendered into. Matching lives here because every answer it produces is
|
|
617
|
+
* instance-shaped — a matched instance, an unmatched one, or a ranking of
|
|
618
|
+
* templates against one path.
|
|
619
|
+
*/
|
|
620
|
+
declare class InstanceDiscoveryService {
|
|
621
|
+
private readonly instanceDiscoveryLocatingService;
|
|
622
|
+
private readonly instanceDiscoveryMatchingService;
|
|
623
|
+
private readonly instanceGroupService;
|
|
624
|
+
private readonly templateDiscoveryService;
|
|
625
|
+
constructor(instanceDiscoveryLocatingService: InstanceDiscoveryLocatingService, instanceDiscoveryMatchingService: InstanceDiscoveryMatchingService, instanceGroupService: InstanceGroupService, templateDiscoveryService: TemplateDiscoveryService);
|
|
626
|
+
/** Weighs one instance against every template, best fit first. */
|
|
627
|
+
private weighInstance;
|
|
628
|
+
/** Builds the substitutions an instance's template is rendered with. */
|
|
629
|
+
buildSubstitutions(instance: Instance): ReturnType<InstanceDiscoveryMatchingService["buildSubstitutions"]>;
|
|
630
|
+
/** Expands instance globs into the instances that exist. */
|
|
631
|
+
findInstances(args: FindInstancesArguments): Instance[];
|
|
632
|
+
/** Resolves every instance to the template, or templates, that explain it. */
|
|
633
|
+
matchInstances(args: {
|
|
634
|
+
instances: Instance[];
|
|
635
|
+
templates: TemplateDefinition[];
|
|
636
|
+
}): ResolvedInstances;
|
|
637
|
+
/** Ranks every template that shares a file with one instance, best first. */
|
|
638
|
+
matchTemplates(args: Parameters<InstanceDiscoveryMatchingService["matchTemplates"]>[0]): ReturnType<InstanceDiscoveryMatchingService["matchTemplates"]>;
|
|
639
|
+
/**
|
|
640
|
+
* Prepares the rendered template and instance document pairs for each matched
|
|
641
|
+
* instance, restricted to the extensions the caller's languages claim.
|
|
642
|
+
*/
|
|
643
|
+
prepareDocuments(args: PrepareDocumentsArguments): PreparedInstanceDocuments[];
|
|
644
|
+
/**
|
|
645
|
+
* Keeps the groups a host with no project graph can actually locate.
|
|
646
|
+
*
|
|
647
|
+
* A group carrying `tags` reads its globs *inside each project the tags
|
|
648
|
+
* select*, so `src/modules/*` has no meaning until a project root is joined
|
|
649
|
+
* to it. This package has no notion of a project, so such a group is not
|
|
650
|
+
* addressed to it and is left out rather than expanded from the working
|
|
651
|
+
* directory — which would match whatever happened to sit at the same
|
|
652
|
+
* relative path and measure it against a template scoped to other projects.
|
|
653
|
+
* `@conformetry/nx` resolves those groups first, so what reaches discovery
|
|
654
|
+
* from there is already workspace-relative and untagged groups are all that
|
|
655
|
+
* is left to read.
|
|
656
|
+
*
|
|
657
|
+
* Dropping them is deliberately silent here and reported by the caller: this
|
|
658
|
+
* is a question about one group, and only a command knows whether the run it
|
|
659
|
+
* was asked for has been left with nothing.
|
|
660
|
+
*
|
|
661
|
+
* Which groups those are is `InstanceGroupService`'s to say, so that this and
|
|
662
|
+
* the set `@conformetry/nx` claims stay exact complements.
|
|
663
|
+
*/
|
|
664
|
+
readWorkspaceGroups(groups: readonly ConformetryInstanceGroup[]): ConformetryInstanceGroup[];
|
|
665
|
+
/** Lists every file a matched instance's template requires it to have. */
|
|
666
|
+
resolveInstanceFiles(instances: MatchedInstance[]): InstanceFile[];
|
|
667
|
+
/**
|
|
668
|
+
* Lists every instance found, paired with the templates that explain it.
|
|
669
|
+
*
|
|
670
|
+
* `templateNames` narrows the pairing rather than the search, so an instance
|
|
671
|
+
* that no named template explains drops out entirely.
|
|
672
|
+
*/
|
|
673
|
+
resolveInventoriedInstances(args: ResolveInventoryArguments): InventoriedInstance[];
|
|
674
|
+
/**
|
|
675
|
+
* Lists every template declared, paired with the instances it explains.
|
|
676
|
+
*
|
|
677
|
+
* `instancePatterns` narrows which instances are considered, which is what
|
|
678
|
+
* turns this into "which templates explain this path". A path can legitimately
|
|
679
|
+
* belong to several templates, so every one that fits is reported.
|
|
680
|
+
*/
|
|
681
|
+
resolveInventoriedTemplates(args: ResolveInventoryArguments): InventoriedTemplate[];
|
|
682
|
+
/** Weighs every instance the globs find against every declared template. */
|
|
683
|
+
takeInventory(args: ResolveInventoryArguments): {
|
|
684
|
+
templates: TemplateDefinition[];
|
|
685
|
+
weighed: {
|
|
686
|
+
instance: Instance;
|
|
687
|
+
pairings: InventoriedPairing[];
|
|
688
|
+
}[];
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/** A file a matched template requires its instance to have. */
|
|
693
|
+
export declare interface InstanceFile {
|
|
694
|
+
readonly instance: MatchedInstance;
|
|
695
|
+
readonly instanceFilePath: string;
|
|
696
|
+
readonly templateFilePath: string;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Reads an instance group's own fields, for every host that resolves one.
|
|
701
|
+
*
|
|
702
|
+
* `ConformetryInstanceGroup` is declared beside this, and how its `tags` are
|
|
703
|
+
* read decides which host owns a group — so the reading lives here rather than
|
|
704
|
+
* in either host. `@conformetry/nx` resolves project-scoped groups against the
|
|
705
|
+
* project graph; a host without one leaves them alone. The two answers must be
|
|
706
|
+
* complements, and nothing would fail if they stopped being: each host would
|
|
707
|
+
* simply take a different set of groups to be its own, and validation would
|
|
708
|
+
* quietly measure the wrong tree.
|
|
709
|
+
*
|
|
710
|
+
* Dependency-free on purpose. Every host reaches it through
|
|
711
|
+
* `ConfigurationService`, so nothing has to be wired up to ask this question —
|
|
712
|
+
* including the install-time bootstrap, which has no project graph to hand
|
|
713
|
+
* anybody.
|
|
714
|
+
*/
|
|
715
|
+
declare class InstanceGroupService {
|
|
716
|
+
constructor();
|
|
717
|
+
/**
|
|
718
|
+
* Whether a group locates its instances inside the hosts its tags select.
|
|
719
|
+
*
|
|
720
|
+
* Non-empty `tags` is the whole rule. An empty array is not a selector — it
|
|
721
|
+
* selects nothing and would silence the group entirely — so it reads as the
|
|
722
|
+
* workspace form, the same as omitting the field.
|
|
723
|
+
*/
|
|
724
|
+
isProjectScoped(group: ConformetryInstanceGroup): boolean;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Minimal JSON Schema fragment used to discover a generator's inputs.
|
|
729
|
+
*
|
|
730
|
+
* Only the parts conformetry actually reads are modelled — `properties` for
|
|
731
|
+
* the input names, and `required` for which ones must be supplied.
|
|
732
|
+
*/
|
|
733
|
+
export declare interface JsonSchemaDefinition {
|
|
734
|
+
[key: string]: unknown;
|
|
735
|
+
properties?: Record<string, unknown>;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/** An instance paired with the template that best explains it. */
|
|
739
|
+
export declare interface MatchedInstance {
|
|
740
|
+
readonly instance: Instance;
|
|
741
|
+
/** How many of the template's files the instance already has. */
|
|
742
|
+
readonly matchedFileCount: number;
|
|
743
|
+
readonly substitutions: Substitutions;
|
|
744
|
+
readonly template: TemplateDefinition;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* A required input that cannot be asked for, because stdin is not a terminal.
|
|
749
|
+
*
|
|
750
|
+
* `prompts` does not fail there — it draws its menu, never resolves, and the
|
|
751
|
+
* process exits 0 having done nothing, which is exactly how this CLI used to
|
|
752
|
+
* hang in non-interactive environments. Naming the flag to pass is the whole
|
|
753
|
+
* remedy, so the message carries it.
|
|
754
|
+
*/
|
|
755
|
+
export declare const missingInputError: (inputName: string) => InputError;
|
|
756
|
+
|
|
757
|
+
/**
|
|
758
|
+
* Raised when a template interpolates a placeholder nobody supplied a value
|
|
759
|
+
* for.
|
|
760
|
+
*
|
|
761
|
+
* Refusing is the only outcome that cannot be mistaken for success: mustache
|
|
762
|
+
* would render the placeholder as an empty string, and because generation and
|
|
763
|
+
* validation render identically, both halves of the loop would lose the same
|
|
764
|
+
* value and agree that nothing was wrong. Section tags are exempt, and why is
|
|
765
|
+
* in this package's README.
|
|
766
|
+
*/
|
|
767
|
+
export declare class MissingSubstitutionError extends Error {
|
|
768
|
+
constructor(args: {
|
|
769
|
+
placeholders: readonly string[];
|
|
770
|
+
subject: string;
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/** Documents prepared for one matched instance. */
|
|
775
|
+
export declare interface PreparedInstanceDocuments {
|
|
776
|
+
readonly documents: PreparedValidationDocument[];
|
|
777
|
+
readonly instance: MatchedInstance;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
/** Arguments for preparing comparison documents for matched instances. */
|
|
781
|
+
export declare interface PrepareDocumentsArguments {
|
|
782
|
+
readonly fileExtensions: string[];
|
|
783
|
+
readonly instances: MatchedInstance[];
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Renders conformetry template placeholders.
|
|
788
|
+
*
|
|
789
|
+
* This is the single owner of template rendering across the workspace.
|
|
790
|
+
* Generation renders templates to create files; validation renders the same
|
|
791
|
+
* templates to compare them against existing files. Both must substitute
|
|
792
|
+
* identically or validation would flag files the generator itself produced,
|
|
793
|
+
* so neither reimplements this.
|
|
794
|
+
*/
|
|
795
|
+
declare class RenderingService {
|
|
796
|
+
constructor();
|
|
797
|
+
/** Refuses to render a template asking for a value nobody supplied. */
|
|
798
|
+
private assertEverySubstitutionSupplied;
|
|
799
|
+
/**
|
|
800
|
+
* Every placeholder a template interpolates, deduplicated.
|
|
801
|
+
*
|
|
802
|
+
* Mustache's own parser reads them, so an interpolation is told from a
|
|
803
|
+
* comment, a partial, and a delimiter change the way mustache tells them.
|
|
804
|
+
* Section names are skipped and their bodies still walked — `{{#field}}` and
|
|
805
|
+
* `{{^field}}` are conditionals, so absence is an answer there — as is the
|
|
806
|
+
* implicit iterator `{{.}}`, which names no field.
|
|
807
|
+
*/
|
|
808
|
+
private collectInterpolatedNames;
|
|
809
|
+
/**
|
|
810
|
+
* Derives the case variants every template can reference from one name.
|
|
811
|
+
*
|
|
812
|
+
* Callers merge their own inputs over this result, so an explicit input of
|
|
813
|
+
* the same key always wins over the derived variant.
|
|
814
|
+
*/
|
|
815
|
+
buildNameSubstitutions(name: string): Substitutions;
|
|
816
|
+
/**
|
|
817
|
+
* Renders template contents with mustache.
|
|
818
|
+
*
|
|
819
|
+
* Full mustache is available — sections, inverted sections, partials — with
|
|
820
|
+
* HTML escaping disabled so substituted values cannot corrupt source code.
|
|
821
|
+
*
|
|
822
|
+
* An interpolated placeholder nobody supplied raises
|
|
823
|
+
* `MissingSubstitutionError`. `subject` is what that error names, so pass
|
|
824
|
+
* the template's path when there is one.
|
|
825
|
+
*/
|
|
826
|
+
renderContent(args: {
|
|
827
|
+
subject?: string;
|
|
828
|
+
substitutions: Substitutions;
|
|
829
|
+
templateContent: string;
|
|
830
|
+
}): string;
|
|
831
|
+
/**
|
|
832
|
+
* Renders a template path with mustache, the same way contents are rendered.
|
|
833
|
+
*
|
|
834
|
+
* Paths once used a `__field__` syntax of their own, on the assumption that
|
|
835
|
+
* braces were not portable across filesystems. They are — and the separate
|
|
836
|
+
* syntax could not tell a placeholder from a Python dunder, so a template
|
|
837
|
+
* shipping `__init__.py` depended on `init` never being a substitution.
|
|
838
|
+
*/
|
|
839
|
+
renderPath(args: {
|
|
840
|
+
subject?: string;
|
|
841
|
+
substitutions: Substitutions;
|
|
842
|
+
templatePath: string;
|
|
843
|
+
}): string;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/** The outcome of resolving a set of instances against a set of templates. */
|
|
847
|
+
export declare interface ResolvedInstances {
|
|
848
|
+
readonly matched: MatchedInstance[];
|
|
849
|
+
readonly unmatched: UnmatchedInstance[];
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
/** Arguments for resolving generator inputs from raw CLI arguments. */
|
|
853
|
+
export declare interface ResolveGeneratorInputsArguments {
|
|
854
|
+
rawArguments: string[];
|
|
855
|
+
schema: JsonSchemaDefinition;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/** Arguments for resolving inputs from values the caller already has. */
|
|
859
|
+
declare interface ResolveInputsFromValuesArguments {
|
|
860
|
+
providedInputs: Record<string, string | undefined>;
|
|
861
|
+
schema: JsonSchemaDefinition;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Arguments for taking an inventory of templates and instances.
|
|
866
|
+
*
|
|
867
|
+
* Both filters narrow the pairing rather than the search, so an entry that
|
|
868
|
+
* survives neither drops out entirely.
|
|
869
|
+
*/
|
|
870
|
+
export declare interface ResolveInventoryArguments {
|
|
871
|
+
readonly configuration: ConformetryConfiguration;
|
|
872
|
+
/** Globs narrowing which instances count; the configured ones when absent. */
|
|
873
|
+
readonly instancePatterns?: string[] | undefined;
|
|
874
|
+
/** Template names narrowing which templates count; all of them when absent. */
|
|
875
|
+
readonly templateNames?: string[] | undefined;
|
|
876
|
+
readonly workingDirectory: string;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/** One schema-backed input being resolved. */
|
|
880
|
+
declare interface SchemaInput {
|
|
881
|
+
readonly inputName: string;
|
|
882
|
+
readonly isRequired: boolean;
|
|
883
|
+
readonly propertySchema: unknown;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Placeholder values applied when rendering a template.
|
|
888
|
+
*
|
|
889
|
+
* Built from the generator name by `RenderingService.buildNameSubstitutions`
|
|
890
|
+
* and then merged with the caller's own inputs, so an explicit input always
|
|
891
|
+
* wins over a derived name variant.
|
|
892
|
+
*/
|
|
893
|
+
export declare type Substitutions = Record<string, string>;
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* One template a picker can offer.
|
|
897
|
+
*
|
|
898
|
+
* Deliberately not `ConformetryGeneratorDefinition`: the picker needs only the
|
|
899
|
+
* name it returns and the description it shows, and asking for the whole
|
|
900
|
+
* definition would tie prompting to the configuration's shape.
|
|
901
|
+
*/
|
|
902
|
+
export declare interface TemplateChoice {
|
|
903
|
+
readonly description?: string | undefined;
|
|
904
|
+
readonly name: string;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/** One template: a directory of mustache files under the templates root. */
|
|
908
|
+
export declare interface TemplateDefinition {
|
|
909
|
+
/** Absolute path to the template's own directory. */
|
|
910
|
+
readonly directoryPath: string;
|
|
911
|
+
/** Absolute paths of every file in the template, sorted. */
|
|
912
|
+
readonly filePaths: string[];
|
|
913
|
+
/** The template's directory name, used to identify it. */
|
|
914
|
+
readonly name: string;
|
|
915
|
+
/**
|
|
916
|
+
* Lowest conformance score an instance of this template may have, from the
|
|
917
|
+
* generator that owns it. Undefined when the generator sets none.
|
|
918
|
+
*/
|
|
919
|
+
readonly threshold?: number;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Reads template folders and maps their files onto instance files.
|
|
924
|
+
*
|
|
925
|
+
* A template's tree is laid over the instance path verbatim, so a template
|
|
926
|
+
* that should produce a folder contains that folder — `{{nameKebabCase}}/…` —
|
|
927
|
+
* rather than the runtime inventing one. That keeps the shape of the output
|
|
928
|
+
* visible in the template itself, and lets one template produce a folder while
|
|
929
|
+
* another produces loose files, with nothing but their contents to say so.
|
|
930
|
+
*
|
|
931
|
+
* Rendering goes through `RenderingService` so a template is substituted here
|
|
932
|
+
* exactly as it was when the generator wrote the file — otherwise validation
|
|
933
|
+
* would report differences the generator itself introduced.
|
|
934
|
+
*/
|
|
935
|
+
declare class TemplateDiscoveryService {
|
|
936
|
+
private readonly renderingService;
|
|
937
|
+
constructor(renderingService: RenderingService);
|
|
938
|
+
/** Lists every file under a directory, recursively and sorted. */
|
|
939
|
+
private collectFilePaths;
|
|
940
|
+
/**
|
|
941
|
+
* Reads one template folder.
|
|
942
|
+
*
|
|
943
|
+
* The folder is the whole definition — nothing but the files it contains is
|
|
944
|
+
* needed to validate against it.
|
|
945
|
+
*/
|
|
946
|
+
collectTemplate(args: {
|
|
947
|
+
name: string;
|
|
948
|
+
templatePath: string;
|
|
949
|
+
threshold?: number | undefined;
|
|
950
|
+
}): TemplateDefinition;
|
|
951
|
+
/**
|
|
952
|
+
* Reads every configured generator's template folder.
|
|
953
|
+
*
|
|
954
|
+
* Every host needs this before it can match anything, and each was resolving
|
|
955
|
+
* the configured paths itself. Resolving them once here keeps one answer to
|
|
956
|
+
* where a generator's template lives.
|
|
957
|
+
*/
|
|
958
|
+
collectTemplates(args: {
|
|
959
|
+
configuration: ConformetryConfiguration;
|
|
960
|
+
workingDirectory: string;
|
|
961
|
+
}): TemplateDefinition[];
|
|
962
|
+
/**
|
|
963
|
+
* Counts how many of a template's files the instance path already has.
|
|
964
|
+
*
|
|
965
|
+
* When a file scope is given, only files inside it count — that is what lets
|
|
966
|
+
* a file glob select a template describing exactly those files rather than
|
|
967
|
+
* the larger template describing the whole directory.
|
|
968
|
+
*/
|
|
969
|
+
countMatchingFiles(args: {
|
|
970
|
+
fileScope?: string[] | undefined;
|
|
971
|
+
instancePath: string;
|
|
972
|
+
substitutions: Substitutions;
|
|
973
|
+
template: TemplateDefinition;
|
|
974
|
+
}): number;
|
|
975
|
+
/**
|
|
976
|
+
* Pairs one template file with its instance, rendering the template.
|
|
977
|
+
*
|
|
978
|
+
* Returns `undefined` when the instance does not exist — that is a missing
|
|
979
|
+
* file, which the file-existence pass reports; a language validator has nothing
|
|
980
|
+
* to compare and should not see the pair at all.
|
|
981
|
+
*/
|
|
982
|
+
prepareDocument(args: {
|
|
983
|
+
instancePath: string;
|
|
984
|
+
substitutions: Substitutions;
|
|
985
|
+
templateDirectoryPath: string;
|
|
986
|
+
templateFilePath: string;
|
|
987
|
+
}): PreparedValidationDocument | undefined;
|
|
988
|
+
/** Maps a template file path to the instance file path it governs. */
|
|
989
|
+
resolveInstanceFilePath(args: {
|
|
990
|
+
instancePath: string;
|
|
991
|
+
substitutions: Substitutions;
|
|
992
|
+
templateDirectoryPath: string;
|
|
993
|
+
templateFilePath: string;
|
|
994
|
+
}): string;
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
/**
|
|
998
|
+
* One template weighed against an instance during matching.
|
|
999
|
+
*
|
|
1000
|
+
* `matchRatio` measures which template *fits*, by file presence alone. It is
|
|
1001
|
+
* deliberately not called a score: an instance's conformance score measures
|
|
1002
|
+
* how well the matched template's contents are honoured, which is a different
|
|
1003
|
+
* number arrived at a different way.
|
|
1004
|
+
*/
|
|
1005
|
+
export declare interface TemplateMatch {
|
|
1006
|
+
readonly matchedFileCount: number;
|
|
1007
|
+
/** Share of the template's files the instance already has, 0 to 1. */
|
|
1008
|
+
readonly matchRatio: number;
|
|
1009
|
+
readonly template: TemplateDefinition;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
/** Raised when the configuration path points to an unsupported file type. */
|
|
1013
|
+
export declare class UnknownConfigurationFileTypeError extends Error {
|
|
1014
|
+
constructor(filePath: string);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/** An instance no template explains well enough to validate against. */
|
|
1018
|
+
export declare interface UnmatchedInstance {
|
|
1019
|
+
readonly instance: Instance;
|
|
1020
|
+
readonly reason: UnmatchedReason;
|
|
1021
|
+
/** Templates that tied, when the reason is `ambiguous`. */
|
|
1022
|
+
readonly tiedTemplateNames: string[];
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
/**
|
|
1026
|
+
* Why an instance could not be matched.
|
|
1027
|
+
*
|
|
1028
|
+
* Both are reported rather than skipped: a glob is the caller asserting these
|
|
1029
|
+
* directories are instances, so an instance matching nothing is a real finding,
|
|
1030
|
+
* and a tie means two templates are indistinguishable.
|
|
1031
|
+
*/
|
|
1032
|
+
export declare type UnmatchedReason = "ambiguous" | "no-match";
|
|
1033
|
+
|
|
1034
|
+
export { }
|