@conformetry/languages 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 +941 -0
- package/dist/src/index.d.ts +977 -0
- package/dist/src/index.js +1184 -0
- package/package.json +69 -0
|
@@ -0,0 +1,977 @@
|
|
|
1
|
+
import { ConfigurationService } from '@conformetry/configuration';
|
|
2
|
+
import { ConformetryDifference } from '@conformetry/core';
|
|
3
|
+
import { ConformetryDifferenceLanguage } from '@conformetry/core';
|
|
4
|
+
import { ConformetryDifferenceType } from '@conformetry/core';
|
|
5
|
+
import { ConformetryLanguageValidator } from '@conformetry/core';
|
|
6
|
+
import { DocumentValidationResult } from '@conformetry/core';
|
|
7
|
+
import { LanguageValidatorDescriptor } from '@conformetry/core';
|
|
8
|
+
import { MatchedInstance } from '@conformetry/configuration';
|
|
9
|
+
import { Node } from 'typescript';
|
|
10
|
+
import { PreparedValidationDocument } from '@conformetry/core';
|
|
11
|
+
import { SourceFile } from 'typescript';
|
|
12
|
+
import { ValidationFileResult } from '@conformetry/core';
|
|
13
|
+
|
|
14
|
+
/** Arguments for building a missing-directory conformance error. */
|
|
15
|
+
declare interface BuildMissingDirectoryErrorArguments {
|
|
16
|
+
readonly instanceDirectoryPath: string;
|
|
17
|
+
readonly templateDirectoryPath: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Arguments for building a missing-file conformance error. */
|
|
21
|
+
declare interface BuildMissingFileErrorArguments {
|
|
22
|
+
readonly instanceFilePath: string;
|
|
23
|
+
readonly templateFilePath: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Arguments for turning a weight pair into a score. */
|
|
27
|
+
export declare interface CalculateScoreArguments {
|
|
28
|
+
/** Combined weight of the requirements the instance failed. */
|
|
29
|
+
readonly failedWeight: number;
|
|
30
|
+
/** Combined weight of the requirements that were checked. */
|
|
31
|
+
readonly totalWeight: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Arguments for checking matched instances against the templates they matched. */
|
|
35
|
+
export declare interface CheckInstanceFilesArguments {
|
|
36
|
+
readonly instances: MatchedInstance[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** What comparing the comments of two source files produced. */
|
|
40
|
+
declare interface CommentComparison {
|
|
41
|
+
readonly missingComments: ExtractedComment[];
|
|
42
|
+
/** Number of template comments checked; each counts as one requirement. */
|
|
43
|
+
readonly totalWeight: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Arguments for walking one level of two markdown trees. */
|
|
47
|
+
declare interface CompareChildrenArguments {
|
|
48
|
+
readonly instanceChildren: MarkdownNode[];
|
|
49
|
+
readonly templateChildren: MarkdownNode[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The outcome of walking one level of two trees. */
|
|
53
|
+
declare interface CompareChildrenResult {
|
|
54
|
+
readonly differences: MarkdownComparisonError[];
|
|
55
|
+
/** Template nodes the level weighed the instance against. */
|
|
56
|
+
readonly totalWeight: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Arguments for comparing the comments of two source files. */
|
|
60
|
+
declare interface CompareCommentsArguments {
|
|
61
|
+
readonly instanceSourceFile: SourceFile;
|
|
62
|
+
readonly templateSourceFile: SourceFile;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Arguments for comparing two JSON values. */
|
|
66
|
+
declare interface CompareJsonArguments {
|
|
67
|
+
readonly instanceValue: JsonValue;
|
|
68
|
+
readonly language: JsonComparisonLanguage;
|
|
69
|
+
readonly pathSegments?: JsonPathSegment[];
|
|
70
|
+
readonly templateValue: JsonValue;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Arguments for walking two syntax trees in parallel. */
|
|
74
|
+
declare interface CompareTreeArguments {
|
|
75
|
+
readonly instanceNode: Node;
|
|
76
|
+
readonly templateNode: Node;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Weight a finding carries when it does not declare one.
|
|
81
|
+
*
|
|
82
|
+
* Right for any leaf requirement — a missing line, a missing comment — which
|
|
83
|
+
* is most of them. Only a finding standing in for a whole subtree needs to say
|
|
84
|
+
* otherwise.
|
|
85
|
+
*/
|
|
86
|
+
export declare const DEFAULT_ERROR_WEIGHT = 1;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Owns construction and narrowing of structured conformance differences.
|
|
90
|
+
*
|
|
91
|
+
* Imported by every validator package so error shapes stay identical across
|
|
92
|
+
* languages, and by the file-existence pass for the file and directory
|
|
93
|
+
* categories.
|
|
94
|
+
*/
|
|
95
|
+
export declare class DifferencesModule {
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Builds and narrows structured conformance differences.
|
|
100
|
+
*
|
|
101
|
+
* Every validator funnels through here so that error wording, the `fix`
|
|
102
|
+
* suggestion, and the location fields stay consistent across languages. The
|
|
103
|
+
* `resolve*` guards additionally narrow untrusted payloads — notably the JSON
|
|
104
|
+
* emitted by the Python validator bridge, whose fields cross a process
|
|
105
|
+
* boundary and cannot be trusted to match the TypeScript types.
|
|
106
|
+
*/
|
|
107
|
+
export declare class DifferencesService {
|
|
108
|
+
constructor();
|
|
109
|
+
/**
|
|
110
|
+
* Builds the error raised when a template directory has no counterpart in
|
|
111
|
+
* the instance tree. Carries no `language`, since a missing directory is not
|
|
112
|
+
* attributable to any one file format.
|
|
113
|
+
*/
|
|
114
|
+
buildMissingDirectoryDifference(args: BuildMissingDirectoryErrorArguments): ConformetryDifference;
|
|
115
|
+
/**
|
|
116
|
+
* Builds the error raised when a template file has no counterpart in the
|
|
117
|
+
* instance tree. This is the one check that runs for every template file
|
|
118
|
+
* regardless of extension, so extension-less files such as `.gitignore` are
|
|
119
|
+
* covered too.
|
|
120
|
+
*/
|
|
121
|
+
buildMissingFileDifference(args: BuildMissingFileErrorArguments): ConformetryDifference;
|
|
122
|
+
/**
|
|
123
|
+
* Narrows an untrusted value to a known error category, falling back to
|
|
124
|
+
* `"code"`. Falling back rather than throwing keeps one malformed error from
|
|
125
|
+
* failing an entire validation run.
|
|
126
|
+
*/
|
|
127
|
+
resolveDifferenceType(value: unknown): ConformetryDifferenceType;
|
|
128
|
+
/**
|
|
129
|
+
* Narrows an untrusted value to a known error language, returning
|
|
130
|
+
* `undefined` when it matches none. Callers omit the field entirely rather
|
|
131
|
+
* than storing a bogus language.
|
|
132
|
+
*/
|
|
133
|
+
resolveErrorLanguage(value: unknown): ConformetryDifferenceLanguage | undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Score given to a document that imposes no requirements at all.
|
|
138
|
+
*
|
|
139
|
+
* An empty template asks for nothing, and nothing is exactly what the instance
|
|
140
|
+
* supplied, so it conforms perfectly. Scoring it zero would be arithmetic
|
|
141
|
+
* leaking into the answer: a run whose templates happen to be empty would fail
|
|
142
|
+
* every threshold while having found no fault with anything.
|
|
143
|
+
*/
|
|
144
|
+
export declare const EMPTY_TEMPLATE_SCORE = 1;
|
|
145
|
+
|
|
146
|
+
/** A comment extracted from a source file, with its offset for ordering. */
|
|
147
|
+
declare interface ExtractedComment {
|
|
148
|
+
readonly position: number;
|
|
149
|
+
readonly text: string;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* What the existence pass found, and how much it asked for.
|
|
154
|
+
*
|
|
155
|
+
* The total counts every declared file, present ones included: a template
|
|
156
|
+
* asking for twenty files and getting nineteen has lost a twentieth of itself,
|
|
157
|
+
* which only the full denominator can say.
|
|
158
|
+
*/
|
|
159
|
+
export declare interface FilesCheckResult {
|
|
160
|
+
readonly fileResults: ValidationFileResult[];
|
|
161
|
+
/** One requirement per file the matched templates declare. */
|
|
162
|
+
readonly totalWeight: number;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Provides file and directory existence checking.
|
|
167
|
+
*
|
|
168
|
+
* Imported by `conformetry-validation`, which runs it before delegating to any
|
|
169
|
+
* language validator — a file that is absent cannot be compared.
|
|
170
|
+
*/
|
|
171
|
+
export declare class FilesModule {
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Checks that every file a project's template declares actually exists.
|
|
176
|
+
*
|
|
177
|
+
* This runs before any language validator, and is the only check that covers
|
|
178
|
+
* *every* template file regardless of extension. A language validator only
|
|
179
|
+
* sees documents whose extension it claims, so files such as `.gitignore`,
|
|
180
|
+
* `.env.default`, and `pyproject.toml` were previously never checked at all —
|
|
181
|
+
* a project could delete them and still validate clean.
|
|
182
|
+
*/
|
|
183
|
+
export declare class FilesService {
|
|
184
|
+
private readonly configurationService;
|
|
185
|
+
private readonly errorsService;
|
|
186
|
+
constructor(configurationService: ConfigurationService, errorsService: DifferencesService);
|
|
187
|
+
/**
|
|
188
|
+
* Reports a path as a missing directory when the template entry lives under
|
|
189
|
+
* a directory that does not exist, and as a missing file otherwise.
|
|
190
|
+
*
|
|
191
|
+
* Reporting the absent directory once is more useful than reporting each of
|
|
192
|
+
* the twenty files inside it.
|
|
193
|
+
*/
|
|
194
|
+
private buildMissingDifference;
|
|
195
|
+
/** Counts how many declared files one directory should hold. */
|
|
196
|
+
private countExpectedFiles;
|
|
197
|
+
/**
|
|
198
|
+
* Reports every file a matched instance's template requires but the instance
|
|
199
|
+
* lacks.
|
|
200
|
+
*
|
|
201
|
+
* Missing directories are collapsed to one finding each, so deleting a whole
|
|
202
|
+
* module reports the directory rather than each file within it.
|
|
203
|
+
*/
|
|
204
|
+
checkInstanceFiles(args: CheckInstanceFilesArguments): FilesCheckResult;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** What structurally comparing two JSON values produced. */
|
|
208
|
+
export declare interface JsonComparison {
|
|
209
|
+
readonly differences: ConformetryDifference[];
|
|
210
|
+
/** Template nodes the walk weighed the instance against. */
|
|
211
|
+
readonly totalWeight: number;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Which validator is asking for the comparison.
|
|
216
|
+
*
|
|
217
|
+
* The walk is shared between JSON files and Jupyter notebooks, and the differences
|
|
218
|
+
* it emits must be attributed to whichever one raised them.
|
|
219
|
+
*/
|
|
220
|
+
declare type JsonComparisonLanguage = "json" | "python";
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Structurally compares two JSON documents.
|
|
224
|
+
*
|
|
225
|
+
* The template is treated as a **subset** requirement, not an exact match: the
|
|
226
|
+
* instance may add keys and array entries freely, but every key and value the
|
|
227
|
+
* template declares must be present. That is what makes a generated file
|
|
228
|
+
* editable after generation without immediately failing validation.
|
|
229
|
+
*
|
|
230
|
+
* Exported from this module so the Jupyter module can reuse it for
|
|
231
|
+
* notebooks, which are JSON documents, rather than duplicating the walk.
|
|
232
|
+
*
|
|
233
|
+
* The walk also counts what it asked for: every template node is one
|
|
234
|
+
* requirement, and a key with no counterpart costs the whole subtree beneath
|
|
235
|
+
* it, so dropping an object scores worse than dropping one of its scalars.
|
|
236
|
+
*/
|
|
237
|
+
export declare class JsonComparisonService {
|
|
238
|
+
private readonly scoringService;
|
|
239
|
+
constructor(scoringService: ScoringService);
|
|
240
|
+
/** Builds one structural error at a JSON path. */
|
|
241
|
+
private buildError;
|
|
242
|
+
/** Merges sibling comparisons into one. */
|
|
243
|
+
private combine;
|
|
244
|
+
/** Matches one required array entry against the instance array. */
|
|
245
|
+
private compareArrayItem;
|
|
246
|
+
/**
|
|
247
|
+
* Compares two arrays.
|
|
248
|
+
*
|
|
249
|
+
* Required scalars must appear somewhere in the instance array, order
|
|
250
|
+
* independent. For a required object, the instance entry that produces the
|
|
251
|
+
* fewest differences is taken as the intended match — an array entry has no key, so
|
|
252
|
+
* there is nothing better to match on.
|
|
253
|
+
*/
|
|
254
|
+
private compareArrays;
|
|
255
|
+
/** Compares two objects, requiring every template key to be present. */
|
|
256
|
+
private compareObjects;
|
|
257
|
+
/**
|
|
258
|
+
* Adds the container's own requirement to what its members contributed, so a
|
|
259
|
+
* matched object weighs exactly what `countNodes` would have charged for it
|
|
260
|
+
* had it been missing.
|
|
261
|
+
*/
|
|
262
|
+
private countContainer;
|
|
263
|
+
/** Counts a JSON value and every value nested inside it. */
|
|
264
|
+
private countNodes;
|
|
265
|
+
/** Renders a path as `scripts.build[0]` for error messages. */
|
|
266
|
+
private formatPath;
|
|
267
|
+
/** Returns whether a value is a plain JSON object. */
|
|
268
|
+
private isJsonObject;
|
|
269
|
+
/** Returns whether a value is a JSON scalar. */
|
|
270
|
+
private isJsonPrimitive;
|
|
271
|
+
/**
|
|
272
|
+
* Picks the candidate comparison that left the least of the template
|
|
273
|
+
* unaccounted for.
|
|
274
|
+
*
|
|
275
|
+
* Weighed by failed weight rather than error count: one finding standing in
|
|
276
|
+
* for a whole missing object is a worse match than two missing scalars.
|
|
277
|
+
*/
|
|
278
|
+
private pickClosestMatch;
|
|
279
|
+
/**
|
|
280
|
+
* Compares a template value against an instance value, returning every way
|
|
281
|
+
* the instance fails to contain what the template requires.
|
|
282
|
+
*/
|
|
283
|
+
compare(args: CompareJsonArguments): JsonComparison;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Provides the JSON language validator.
|
|
288
|
+
*
|
|
289
|
+
* `JsonComparisonService` is exported as well, because notebooks are JSON
|
|
290
|
+
* documents and the Jupyter module reuses the same structural walk.
|
|
291
|
+
*/
|
|
292
|
+
export declare class JsonModule {
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** One step of a JSON path: an object key or an array index. */
|
|
296
|
+
declare type JsonPathSegment = number | string;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Checks that a JSON or JSONC file contains everything its template declares.
|
|
300
|
+
*
|
|
301
|
+
* Parsing goes through `jsonc-parser` so a `tsconfig.json` with comments is
|
|
302
|
+
* read the same way TypeScript reads it.
|
|
303
|
+
*/
|
|
304
|
+
export declare class JsonService implements ConformetryLanguageValidator {
|
|
305
|
+
private readonly jsonComparisonService;
|
|
306
|
+
constructor(jsonComparisonService: JsonComparisonService);
|
|
307
|
+
readonly descriptor: LanguageValidatorDescriptor;
|
|
308
|
+
/** Reports every key or value the template requires and the instance lacks. */
|
|
309
|
+
validateDocument(document: PreparedValidationDocument): DocumentValidationResult;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/** Any JSON value, used when structurally comparing two documents. */
|
|
313
|
+
export declare type JsonValue = boolean | JsonValue[] | null | number | string | {
|
|
314
|
+
[key: string]: JsonValue;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Provides the Jupyter notebook validator.
|
|
319
|
+
*
|
|
320
|
+
* Composes the JSON, markdown, and Python validators rather than
|
|
321
|
+
* reimplementing any of them — a notebook is all three formats at once.
|
|
322
|
+
*/
|
|
323
|
+
export declare class JupyterModule {
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Reads the `.ipynb` format into something comparable.
|
|
328
|
+
*
|
|
329
|
+
* A notebook is JSON, but its meaning lives in the cells: markdown prose and
|
|
330
|
+
* Python code stored as line arrays. This service turns that back into text so
|
|
331
|
+
* the markdown and Python validators can work on it unchanged.
|
|
332
|
+
*/
|
|
333
|
+
export declare class JupyterNotebookService {
|
|
334
|
+
constructor();
|
|
335
|
+
/** Buckets each cell's text by kind, preserving notebook order. */
|
|
336
|
+
private groupSourcesByKind;
|
|
337
|
+
/** Narrows a cell's declared kind, defaulting to raw. */
|
|
338
|
+
private readCellKind;
|
|
339
|
+
/** Joins a cell's `source` line array back into text. */
|
|
340
|
+
private readCellSource;
|
|
341
|
+
/**
|
|
342
|
+
* Pairs template cells with instance cells of the same kind, in order.
|
|
343
|
+
*
|
|
344
|
+
* Cells are positional — a notebook has no cell identifiers — so the nth
|
|
345
|
+
* markdown cell is compared with the nth markdown cell. An instance may add
|
|
346
|
+
* cells at the end; it may not drop one the template declares.
|
|
347
|
+
*/
|
|
348
|
+
pairCells(args: {
|
|
349
|
+
instanceNotebook: ParsedNotebook;
|
|
350
|
+
templateNotebook: ParsedNotebook;
|
|
351
|
+
}): {
|
|
352
|
+
missingCells: PairedCells[];
|
|
353
|
+
pairedCells: PairedCells[];
|
|
354
|
+
};
|
|
355
|
+
/**
|
|
356
|
+
* Parses notebook JSON, tolerating a malformed file by reporting no cells.
|
|
357
|
+
*
|
|
358
|
+
* A notebook that will not parse is reported by the structural pass, so
|
|
359
|
+
* throwing here would only duplicate that as a crash.
|
|
360
|
+
*/
|
|
361
|
+
parseNotebook(content: string): ParsedNotebook;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Checks that a Jupyter notebook conforms to its template.
|
|
366
|
+
*
|
|
367
|
+
* A notebook is three formats at once, so this validator composes the three
|
|
368
|
+
* that already exist rather than reimplementing any: JSON for the envelope,
|
|
369
|
+
* markdown for prose cells, and the Python bridge for code cells. Validating a
|
|
370
|
+
* code cell through Python's own parser is what lets a notebook be re-run and
|
|
371
|
+
* reformatted without failing.
|
|
372
|
+
*/
|
|
373
|
+
export declare class JupyterService implements ConformetryLanguageValidator {
|
|
374
|
+
private readonly jsonComparisonService;
|
|
375
|
+
private readonly jupyterNotebookService;
|
|
376
|
+
private readonly markdownService;
|
|
377
|
+
private readonly pythonBridgeService;
|
|
378
|
+
constructor(jsonComparisonService: JsonComparisonService, jupyterNotebookService: JupyterNotebookService, markdownService: MarkdownService, pythonBridgeService: PythonBridgeService);
|
|
379
|
+
readonly descriptor: LanguageValidatorDescriptor;
|
|
380
|
+
/** Prefixes an error's message so a reader knows which cell it came from. */
|
|
381
|
+
private attributeToCell;
|
|
382
|
+
/** Reduces a notebook to the envelope keys that are compared structurally. */
|
|
383
|
+
private readEnvelope;
|
|
384
|
+
/** Validates one paired cell with the validator matching its kind. */
|
|
385
|
+
private validateCell;
|
|
386
|
+
/**
|
|
387
|
+
* Weighs a cell the notebook does not have.
|
|
388
|
+
*
|
|
389
|
+
* Measured by comparing the template cell against an empty instance, which
|
|
390
|
+
* is exactly what "none of this is present" means. Guessing a flat 1 instead
|
|
391
|
+
* would let a notebook drop a forty-line code cell for the same price as an
|
|
392
|
+
* empty one, and mixing in a line count would put a unit in the denominator
|
|
393
|
+
* that nothing else uses.
|
|
394
|
+
*/
|
|
395
|
+
private weighMissingCell;
|
|
396
|
+
/** Reports every notebook difference: envelope, missing cells, cell contents. */
|
|
397
|
+
validateDocument(document: PreparedValidationDocument): DocumentValidationResult;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* The one module a host imports to get every Language.
|
|
402
|
+
*
|
|
403
|
+
* Wiring the Languages individually is still possible — each has its own
|
|
404
|
+
* module — but a host almost always wants all of them, because the Fallback
|
|
405
|
+
* makes the text Language a floor under every run rather than an option.
|
|
406
|
+
*/
|
|
407
|
+
export declare class LanguagesModule {
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Answers which Languages a run needs, and applies the Fallback.
|
|
412
|
+
*
|
|
413
|
+
* The Languages are injected rather than looked up by name, so this is the
|
|
414
|
+
* only place one has to be registered: adding a Language means adding it to
|
|
415
|
+
* `claimingLanguages` below, and its own descriptor says which extensions it
|
|
416
|
+
* claims. There is no second list of extensions to keep in step.
|
|
417
|
+
*/
|
|
418
|
+
export declare class LanguagesService {
|
|
419
|
+
private readonly jsonService;
|
|
420
|
+
private readonly jupyterService;
|
|
421
|
+
private readonly markdownService;
|
|
422
|
+
private readonly pythonService;
|
|
423
|
+
private readonly textService;
|
|
424
|
+
private readonly typescriptService;
|
|
425
|
+
constructor(jsonService: JsonService, jupyterService: JupyterService, markdownService: MarkdownService, pythonService: PythonService, textService: TextService, typescriptService: TypescriptService);
|
|
426
|
+
/**
|
|
427
|
+
* Every Language that claims extensions of its own, in report order.
|
|
428
|
+
*
|
|
429
|
+
* A method rather than a field, because the injected engines are assigned
|
|
430
|
+
* in the constructor and field initializers have already run by then.
|
|
431
|
+
*
|
|
432
|
+
* The text Language is deliberately absent. It is the Fallback, reached
|
|
433
|
+
* through `widenFallback` below, so it joins a run only when some extension
|
|
434
|
+
* needs it rather than on account of its own `.txt`.
|
|
435
|
+
*/
|
|
436
|
+
private claimingLanguages;
|
|
437
|
+
/**
|
|
438
|
+
* The text Language, widened to also claim the extensions nothing else did.
|
|
439
|
+
*
|
|
440
|
+
* Widening the descriptor rather than special-casing the dispatch means the
|
|
441
|
+
* caller routes documents by extension exactly as it does for every other
|
|
442
|
+
* Language.
|
|
443
|
+
*/
|
|
444
|
+
private widenFallback;
|
|
445
|
+
/**
|
|
446
|
+
* Resolves a validator for every extension in play.
|
|
447
|
+
*
|
|
448
|
+
* A Language is returned when the run holds at least one extension it
|
|
449
|
+
* claims, so a run over JSON alone never reports a TypeScript result it had
|
|
450
|
+
* nothing to say about. An extension nobody claims falls back to text,
|
|
451
|
+
* compared line by line, so no template file goes unchecked — silently
|
|
452
|
+
* skipping one would check less than the caller believes.
|
|
453
|
+
*/
|
|
454
|
+
resolveValidators(args: ResolveValidatorsArguments): ConformetryLanguageValidator[];
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/** A required markdown node the instance does not contain. */
|
|
458
|
+
declare interface MarkdownComparisonError {
|
|
459
|
+
/** 1-based line in the instance where the node was expected. */
|
|
460
|
+
readonly instanceLine: number | undefined;
|
|
461
|
+
readonly nodeType: string;
|
|
462
|
+
readonly text: string;
|
|
463
|
+
/** Template nodes this one finding stands in for — the missing subtree. */
|
|
464
|
+
readonly weight: number;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Provides the markdown language validator.
|
|
469
|
+
*
|
|
470
|
+
* Imported by the Languages module, and by the Jupyter module, which reuses
|
|
471
|
+
* it for a notebook's markdown cells.
|
|
472
|
+
*/
|
|
473
|
+
export declare class MarkdownModule {
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* The subset of an mdast node this validator reads.
|
|
478
|
+
*
|
|
479
|
+
* Declared structurally rather than importing mdast's own types because only
|
|
480
|
+
* these fields participate in matching, and a narrow shape keeps the
|
|
481
|
+
* comparison honest about what it actually inspects.
|
|
482
|
+
*/
|
|
483
|
+
declare interface MarkdownNode {
|
|
484
|
+
readonly alt?: string;
|
|
485
|
+
readonly children?: MarkdownNode[];
|
|
486
|
+
readonly depth?: number;
|
|
487
|
+
readonly lang?: string;
|
|
488
|
+
readonly ordered?: boolean;
|
|
489
|
+
readonly position?: {
|
|
490
|
+
readonly end?: {
|
|
491
|
+
readonly line?: number;
|
|
492
|
+
};
|
|
493
|
+
};
|
|
494
|
+
readonly type: string;
|
|
495
|
+
readonly url?: string;
|
|
496
|
+
readonly value?: string;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Decides whether two markdown nodes are "the same node".
|
|
501
|
+
*
|
|
502
|
+
* Each node type has its own notion of identity: a heading is its depth plus
|
|
503
|
+
* its text, a link is its URL plus its text, a table is its column count. This
|
|
504
|
+
* is what lets a template require *a table with three columns* without
|
|
505
|
+
* dictating its contents.
|
|
506
|
+
*/
|
|
507
|
+
export declare class MarkdownNodesService {
|
|
508
|
+
constructor();
|
|
509
|
+
/**
|
|
510
|
+
* How to decide that two nodes of a given type are "the same node".
|
|
511
|
+
*
|
|
512
|
+
* A table rather than a switch so each rule stays its own small function —
|
|
513
|
+
* one branch per markdown type in a single method is unreadable, and the
|
|
514
|
+
* types that need no special rule fall through to a text comparison.
|
|
515
|
+
*/
|
|
516
|
+
private readonly matchersByType;
|
|
517
|
+
/** Counts a table's columns from its first row. */
|
|
518
|
+
private readColumnCount;
|
|
519
|
+
/** Compares two optional string fields, treating absent as empty. */
|
|
520
|
+
private sameField;
|
|
521
|
+
/**
|
|
522
|
+
* Counts a node and every countable node beneath it.
|
|
523
|
+
*
|
|
524
|
+
* This is what a missing node costs. Comparison reports a vanished section
|
|
525
|
+
* once, but the template asked for the section and everything inside it, so
|
|
526
|
+
* weighing the finding by its subtree keeps a deleted table from scoring the
|
|
527
|
+
* same as a deleted heading.
|
|
528
|
+
*
|
|
529
|
+
* Skipped types are excluded on both sides of the fraction, so nodes the
|
|
530
|
+
* comparison never checks cannot dilute a score.
|
|
531
|
+
*/
|
|
532
|
+
countSubtree(node: MarkdownNode): number;
|
|
533
|
+
/** Narrows a raw mdast child list to the nodes this validator understands. */
|
|
534
|
+
filterNodes(children: readonly unknown[]): MarkdownNode[];
|
|
535
|
+
/** Returns whether an instance node satisfies a template node. */
|
|
536
|
+
matches(args: {
|
|
537
|
+
instanceNode: MarkdownNode;
|
|
538
|
+
templateNode: MarkdownNode;
|
|
539
|
+
}): boolean;
|
|
540
|
+
/** Reads a node's children, or an empty list for a leaf. */
|
|
541
|
+
readChildren(node: MarkdownNode): MarkdownNode[];
|
|
542
|
+
/** Reads a node's rendered plain text. */
|
|
543
|
+
readText(node: MarkdownNode): string;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Checks that a markdown file contains every structure its template declares.
|
|
548
|
+
*
|
|
549
|
+
* Comparison is structural rather than textual: headings, code fences, links,
|
|
550
|
+
* and tables are matched as mdast nodes, so reformatting or reflowing prose
|
|
551
|
+
* does not fail validation while deleting a required section does.
|
|
552
|
+
*/
|
|
553
|
+
export declare class MarkdownService implements ConformetryLanguageValidator {
|
|
554
|
+
private readonly markdownNodesService;
|
|
555
|
+
private readonly markdownTreeService;
|
|
556
|
+
constructor(markdownNodesService: MarkdownNodesService, markdownTreeService: MarkdownTreeService);
|
|
557
|
+
/** GitHub-flavored so tables and task lists parse as their own node types. */
|
|
558
|
+
private readonly processor;
|
|
559
|
+
readonly descriptor: LanguageValidatorDescriptor;
|
|
560
|
+
/** Reports every markdown structure the template requires and the file lacks. */
|
|
561
|
+
validateDocument(document: PreparedValidationDocument): DocumentValidationResult;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Walks two markdown trees and reports what the template requires but the
|
|
566
|
+
* instance lacks.
|
|
567
|
+
*
|
|
568
|
+
* The walk is order-preserving but not position-locked: each template node is
|
|
569
|
+
* matched against any instance sibling, and the last match anchors the error
|
|
570
|
+
* location for whatever follows. A document may therefore add sections freely,
|
|
571
|
+
* as long as it still contains everything the template declares.
|
|
572
|
+
*
|
|
573
|
+
* The walk also counts what it asked for: every template node it weighs is one
|
|
574
|
+
* requirement, and a node with no counterpart costs its whole subtree.
|
|
575
|
+
*/
|
|
576
|
+
export declare class MarkdownTreeService {
|
|
577
|
+
private readonly markdownNodesService;
|
|
578
|
+
private readonly scoringService;
|
|
579
|
+
constructor(markdownNodesService: MarkdownNodesService, scoringService: ScoringService);
|
|
580
|
+
/** Describes a template node the instance does not contain. */
|
|
581
|
+
private buildError;
|
|
582
|
+
/**
|
|
583
|
+
* Matches a container node, then descends into it.
|
|
584
|
+
*
|
|
585
|
+
* Several instance nodes may match the container shape — two lists, say — so
|
|
586
|
+
* the one whose children satisfy the most of the template is chosen.
|
|
587
|
+
*/
|
|
588
|
+
private compareContainer;
|
|
589
|
+
/** Matches a leaf node on its own identity, without descending. */
|
|
590
|
+
private compareLeaf;
|
|
591
|
+
/** Finds every instance sibling satisfying the template node. */
|
|
592
|
+
private findCandidates;
|
|
593
|
+
/** Compares one level of two trees, descending into containers. */
|
|
594
|
+
compareChildren(args: CompareChildrenArguments): CompareChildrenResult;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* One notebook cell, in the subset of the `.ipynb` schema this validator uses.
|
|
599
|
+
*
|
|
600
|
+
* `source` is a list of lines with their newlines retained, which is how
|
|
601
|
+
* Jupyter stores it.
|
|
602
|
+
*/
|
|
603
|
+
declare interface NotebookCell {
|
|
604
|
+
readonly cell_type?: unknown;
|
|
605
|
+
readonly source?: unknown;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/** The cell kinds this validator understands. */
|
|
609
|
+
declare type NotebookCellKind = "code" | "markdown" | "raw";
|
|
610
|
+
|
|
611
|
+
/** A pair of template and instance cells of the same kind, in order. */
|
|
612
|
+
declare interface PairedCells {
|
|
613
|
+
readonly index: number;
|
|
614
|
+
readonly instanceSource: string;
|
|
615
|
+
readonly kind: NotebookCellKind;
|
|
616
|
+
readonly templateSource: string;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/** A notebook reduced to the parts that are validated. */
|
|
620
|
+
declare interface ParsedNotebook {
|
|
621
|
+
readonly cells: NotebookCell[];
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/** The highest possible score: every template requirement is honoured. */
|
|
625
|
+
export declare const PERFECT_SCORE = 1;
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Runs Python conformance checks through the Python interpreter.
|
|
629
|
+
*
|
|
630
|
+
* Python's syntax tree is only available from Python, so structural validation
|
|
631
|
+
* shells out to a small module shipped inside this package rather than
|
|
632
|
+
* approximating it with a line comparison. The subprocess is synchronous
|
|
633
|
+
* because callers are, and because one file's validation has nothing to
|
|
634
|
+
* overlap with.
|
|
635
|
+
*/
|
|
636
|
+
export declare class PythonBridgeService {
|
|
637
|
+
private readonly errorsService;
|
|
638
|
+
private readonly scoringService;
|
|
639
|
+
constructor(errorsService: DifferencesService, scoringService: ScoringService);
|
|
640
|
+
/**
|
|
641
|
+
* Directory holding the `python` package, resolved from this module rather
|
|
642
|
+
* than from the workspace root, so the bridge is found the same way whether
|
|
643
|
+
* conformetry runs from a checkout or from `node_modules`.
|
|
644
|
+
*/
|
|
645
|
+
private readonly pythonRootPath;
|
|
646
|
+
/**
|
|
647
|
+
* Wraps a bridge failure as a reportable error rather than throwing.
|
|
648
|
+
*
|
|
649
|
+
* Weighed as a single failed requirement out of one: the file could not be
|
|
650
|
+
* checked at all, so claiming any particular proportion of it conforms would
|
|
651
|
+
* be an invention.
|
|
652
|
+
*/
|
|
653
|
+
private buildBridgeError;
|
|
654
|
+
/** Reads the optional location fields, omitting any the bridge left out. */
|
|
655
|
+
private readLocations;
|
|
656
|
+
/** Narrows an untrusted numeric field from the bridge payload. */
|
|
657
|
+
private readNumber;
|
|
658
|
+
/** Narrows an untrusted string field from the bridge payload. */
|
|
659
|
+
private readString;
|
|
660
|
+
/** Reads the optional expected and actual values. */
|
|
661
|
+
private readValues;
|
|
662
|
+
/** Maps one snake_case bridge error onto the shared error shape. */
|
|
663
|
+
private toConformetryDifference;
|
|
664
|
+
/**
|
|
665
|
+
* Compares one Python source against its rendered template.
|
|
666
|
+
*
|
|
667
|
+
* A missing interpreter, a crashed bridge, or unreadable output are all
|
|
668
|
+
* reported as conformance differences: they mean this file could not be checked,
|
|
669
|
+
* which the run should surface, but they must not abort validation of every
|
|
670
|
+
* other file.
|
|
671
|
+
*/
|
|
672
|
+
validatePythonSource(args: RunPythonBridgeArguments): DocumentValidationResult;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* Provides the Python language validator.
|
|
677
|
+
*
|
|
678
|
+
* `PythonBridgeService` is exported as well, because a notebook's code cells
|
|
679
|
+
* are Python and the Jupyter module validates them through the same bridge.
|
|
680
|
+
*/
|
|
681
|
+
export declare class PythonModule {
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Checks that a Python file declares everything its template requires.
|
|
686
|
+
*
|
|
687
|
+
* Comparison is structural, through Python's own `ast` module, so reformatting
|
|
688
|
+
* a file or reordering its declarations does not fail validation while
|
|
689
|
+
* deleting a required class or function does.
|
|
690
|
+
*/
|
|
691
|
+
export declare class PythonService implements ConformetryLanguageValidator {
|
|
692
|
+
private readonly pythonBridgeService;
|
|
693
|
+
constructor(pythonBridgeService: PythonBridgeService);
|
|
694
|
+
readonly descriptor: LanguageValidatorDescriptor;
|
|
695
|
+
/** Reports every declaration and comment the template requires. */
|
|
696
|
+
validateDocument(document: PreparedValidationDocument): DocumentValidationResult;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/** Arguments for resolving the Languages one validation run needs. */
|
|
700
|
+
export declare interface ResolveValidatorsArguments {
|
|
701
|
+
/**
|
|
702
|
+
* Every distinct file extension the run's templates declare, including the
|
|
703
|
+
* leading dot. Extensions no Language claims are routed to the Fallback.
|
|
704
|
+
*/
|
|
705
|
+
readonly extensions: string[];
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/** Arguments for one bridge invocation. */
|
|
709
|
+
declare interface RunPythonBridgeArguments {
|
|
710
|
+
readonly filename: string;
|
|
711
|
+
readonly instance: string;
|
|
712
|
+
/** Template source, already rendered on the TypeScript side. */
|
|
713
|
+
readonly template: string;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* Owns the conformance arithmetic every validator and host would otherwise
|
|
718
|
+
* repeat: what a finding weighs, and what a weight pair scores.
|
|
719
|
+
*/
|
|
720
|
+
export declare class ScoringModule {
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Turns weights into conformance scores.
|
|
725
|
+
*
|
|
726
|
+
* The arithmetic is small but it is the same arithmetic in seven places — one
|
|
727
|
+
* per language package, plus the file-existence pass and the run aggregate —
|
|
728
|
+
* and it has two edge cases worth getting right once: the default weight of a
|
|
729
|
+
* finding that declares none, and an empty template whose denominator is zero.
|
|
730
|
+
*/
|
|
731
|
+
export declare class ScoringService {
|
|
732
|
+
constructor();
|
|
733
|
+
/**
|
|
734
|
+
* Returns the share of the checked requirements the instance honoured, from
|
|
735
|
+
* 0 to 1.
|
|
736
|
+
*
|
|
737
|
+
* Clamped at both ends. A validator that double-counts an overlapping
|
|
738
|
+
* requirement could otherwise report a failed weight above the total and
|
|
739
|
+
* produce a negative score, which would read as a much worse instance than
|
|
740
|
+
* one that is simply entirely wrong.
|
|
741
|
+
*/
|
|
742
|
+
calculateScore(args: CalculateScoreArguments): number;
|
|
743
|
+
/** Adds up what a set of findings costs, defaulting each to its own weight. */
|
|
744
|
+
sumWeights(differences: readonly WeightedFinding[]): number;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Provides the text validator service.
|
|
749
|
+
*/
|
|
750
|
+
export declare class TextModule {
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
/**
|
|
754
|
+
* Checks that a text file contains every line its template requires.
|
|
755
|
+
*
|
|
756
|
+
* Matching is duplicate-aware: a template line that appears twice must appear
|
|
757
|
+
* twice in the instance. Order is not enforced, so a file may add lines
|
|
758
|
+
* anywhere — the template is a lower bound, not an exact specification.
|
|
759
|
+
*/
|
|
760
|
+
export declare class TextService implements ConformetryLanguageValidator {
|
|
761
|
+
constructor();
|
|
762
|
+
readonly descriptor: LanguageValidatorDescriptor;
|
|
763
|
+
/** Counts how many times each line occurs, for duplicate-aware matching. */
|
|
764
|
+
private countLines;
|
|
765
|
+
/** Finds template lines the instance does not supply often enough. */
|
|
766
|
+
private findMissingLines;
|
|
767
|
+
/**
|
|
768
|
+
* Reports every template line missing from the instance.
|
|
769
|
+
*
|
|
770
|
+
* Every template line is one requirement, blank ones included: this
|
|
771
|
+
* validator matches them literally, so a blank line the instance does not
|
|
772
|
+
* supply is a real miss and counting it keeps the denominator honest.
|
|
773
|
+
*/
|
|
774
|
+
validateDocument(document: PreparedValidationDocument): DocumentValidationResult;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/** What comparing two syntax trees produced. */
|
|
778
|
+
declare interface TreeComparison {
|
|
779
|
+
readonly differences: TypescriptComparisonError[];
|
|
780
|
+
/** Number of template nodes the walk weighed the instance against. */
|
|
781
|
+
readonly totalWeight: number;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/**
|
|
785
|
+
* Checks that a file carries the comments its template requires, in order.
|
|
786
|
+
*
|
|
787
|
+
* Order matters here in a way it does not for declarations: the section
|
|
788
|
+
* markers (`🏗 Dependency Injection`, `🔏 Private Methods`, …) are comments,
|
|
789
|
+
* and a file that has them all but in the wrong order has not adopted the
|
|
790
|
+
* layout. Matching is therefore a subsequence check, not a set check.
|
|
791
|
+
*/
|
|
792
|
+
export declare class TypescriptCommentsService {
|
|
793
|
+
constructor();
|
|
794
|
+
/**
|
|
795
|
+
* Reports each template comment absent from the instance, or present but
|
|
796
|
+
* out of order relative to the comments before it.
|
|
797
|
+
*
|
|
798
|
+
* Every template comment is one requirement, whatever its length: a section
|
|
799
|
+
* marker is a marker. That is reported alongside the misses so a document
|
|
800
|
+
* whose comments are all present still contributes them to its score.
|
|
801
|
+
*/
|
|
802
|
+
compareComments(args: CompareCommentsArguments): CommentComparison;
|
|
803
|
+
/**
|
|
804
|
+
* Collects every comment in a source file, in source order.
|
|
805
|
+
*
|
|
806
|
+
* Walks down to individual tokens and reads the trivia preceding each one.
|
|
807
|
+
* Every comment precedes some token, including the closing brace of a class,
|
|
808
|
+
* which is what makes this complete.
|
|
809
|
+
*
|
|
810
|
+
* Two simpler approaches fail here. Reading leading and trailing ranges off
|
|
811
|
+
* *statement* nodes misses any comment bordering no node — notably the
|
|
812
|
+
* section markers between the last class member and the closing brace, where
|
|
813
|
+
* `🔏 Private Methods` and `🌎 Public Methods` live in an otherwise empty
|
|
814
|
+
* service; those markers were silently unenforceable. Scanning the raw token
|
|
815
|
+
* stream instead loses sync on template literals, because a substitution's
|
|
816
|
+
* closing brace needs an explicit re-scan, and everything after the first
|
|
817
|
+
* template substitution is mis-tokenized.
|
|
818
|
+
*/
|
|
819
|
+
extractComments(sourceFile: SourceFile): ExtractedComment[];
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/** A declaration the template requires and the instance does not contain. */
|
|
823
|
+
declare interface TypescriptComparisonError {
|
|
824
|
+
/** Offset in the instance file to anchor the error at, when known. */
|
|
825
|
+
readonly instancePosition: number | undefined;
|
|
826
|
+
/** Syntax-kind label, e.g. `ClassDeclaration`. */
|
|
827
|
+
readonly kindLabel: string;
|
|
828
|
+
/** The node's key — an import specifier, a member name — when it has one. */
|
|
829
|
+
readonly nodeKey: string | undefined;
|
|
830
|
+
/** Offset in the rendered template where the requirement is declared. */
|
|
831
|
+
readonly templatePosition: number | undefined;
|
|
832
|
+
/** Template nodes this one finding stands in for — the missing subtree. */
|
|
833
|
+
readonly weight: number;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* Provides the TypeScript language validator.
|
|
838
|
+
*
|
|
839
|
+
* Split into node keying, tree walking, and comment comparison so each concern
|
|
840
|
+
* stays independently testable — the same decomposition the previous
|
|
841
|
+
* conformance tool used.
|
|
842
|
+
*/
|
|
843
|
+
export declare class TypescriptModule {
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* Derives a stable identity for a syntax node.
|
|
848
|
+
*
|
|
849
|
+
* Matching by key rather than by position is what lets a file reorder its
|
|
850
|
+
* members, or add new ones, without failing validation: an import is
|
|
851
|
+
* identified by its module specifier, a member by its name, a decorator by its
|
|
852
|
+
* dotted callee. Nodes with no meaningful key fall back to matching on syntax
|
|
853
|
+
* kind alone.
|
|
854
|
+
*/
|
|
855
|
+
export declare class TypescriptNodesService {
|
|
856
|
+
constructor();
|
|
857
|
+
/** Builds a dotted name such as `Nest.Injectable` from a callee expression. */
|
|
858
|
+
private buildDottedName;
|
|
859
|
+
/** Returns whether a value looks like a syntax node. */
|
|
860
|
+
private isNode;
|
|
861
|
+
/** Keys a decorator by its callee, so `@Injectable()` matches `@Injectable`. */
|
|
862
|
+
private readDecoratorKey;
|
|
863
|
+
/** Keys an export by its module specifier, when it re-exports one. */
|
|
864
|
+
private readExportKey;
|
|
865
|
+
/**
|
|
866
|
+
* Keys a call statement by its callee plus its first literal argument, so
|
|
867
|
+
* two `describe("...")` blocks are told apart by their subject.
|
|
868
|
+
*/
|
|
869
|
+
private readExpressionStatementKey;
|
|
870
|
+
/** Keys an import by its module specifier. */
|
|
871
|
+
private readImportKey;
|
|
872
|
+
/** Reads a literal's text, for nodes that are themselves a value. */
|
|
873
|
+
private readLiteralKey;
|
|
874
|
+
/** Reads a declaration's own name, when it has one. */
|
|
875
|
+
private readNamedKey;
|
|
876
|
+
/**
|
|
877
|
+
* Counts a node and everything beneath it.
|
|
878
|
+
*
|
|
879
|
+
* This is what a missing declaration costs. Comparison reports a vanished
|
|
880
|
+
* class once, but the template asked for the class *and* every member inside
|
|
881
|
+
* it, so weighing that finding by its subtree is what keeps a deleted class
|
|
882
|
+
* from scoring the same as a deleted import.
|
|
883
|
+
*/
|
|
884
|
+
countSubtree(node: Node): number;
|
|
885
|
+
/** Reads a node's direct children, skipping the end-of-file token. */
|
|
886
|
+
readChildren(node: Node): Node[];
|
|
887
|
+
/**
|
|
888
|
+
* Reads a node's identity, or `null` when it has none.
|
|
889
|
+
*
|
|
890
|
+
* A `null` key means the node can only be matched by syntax kind — which is
|
|
891
|
+
* why an anonymous statement is satisfied by any statement of that kind.
|
|
892
|
+
*/
|
|
893
|
+
readKey(node: Node): null | string;
|
|
894
|
+
/** Reads a node's syntax-kind label, for error messages. */
|
|
895
|
+
readKindLabel(node: Node): string;
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Checks that a TypeScript file declares everything its template requires.
|
|
900
|
+
*
|
|
901
|
+
* Two independent checks run over the same parse: the syntax tree, which
|
|
902
|
+
* verifies imports, decorators, classes, and members exist; and the comments,
|
|
903
|
+
* which verify the section markers appear in the prescribed order.
|
|
904
|
+
*/
|
|
905
|
+
export declare class TypescriptService implements ConformetryLanguageValidator {
|
|
906
|
+
private readonly typeScriptCommentsService;
|
|
907
|
+
private readonly typeScriptTreeService;
|
|
908
|
+
constructor(typeScriptCommentsService: TypescriptCommentsService, typeScriptTreeService: TypescriptTreeService);
|
|
909
|
+
readonly descriptor: LanguageValidatorDescriptor;
|
|
910
|
+
/** Parses source text, choosing the dialect from the filename. */
|
|
911
|
+
private parseSourceFile;
|
|
912
|
+
/** Converts a source offset into a 1-based line and column. */
|
|
913
|
+
private readLocation;
|
|
914
|
+
/** Compares the comments and describes each missing section marker. */
|
|
915
|
+
private validateComments;
|
|
916
|
+
/** Compares the syntax trees and describes each missing declaration. */
|
|
917
|
+
private validateStructure;
|
|
918
|
+
/**
|
|
919
|
+
* Reports every declaration and comment the template requires.
|
|
920
|
+
*
|
|
921
|
+
* The two passes weigh independent things — structure counts syntax nodes,
|
|
922
|
+
* comments count section markers — so their totals add rather than one
|
|
923
|
+
* subsuming the other.
|
|
924
|
+
*/
|
|
925
|
+
validateDocument(document: PreparedValidationDocument): DocumentValidationResult;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/**
|
|
929
|
+
* Walks two syntax trees in parallel and reports what the template requires
|
|
930
|
+
* but the instance does not contain.
|
|
931
|
+
*
|
|
932
|
+
* The template is a **structural subset** requirement: every declaration it
|
|
933
|
+
* makes must exist somewhere in the instance, but the instance may add
|
|
934
|
+
* anything and may order its members freely.
|
|
935
|
+
*
|
|
936
|
+
* The walk also counts what it asked for. Every template node visited is one
|
|
937
|
+
* requirement, and a node with no counterpart costs its whole subtree, so the
|
|
938
|
+
* weight of a finding is proportional to how much of the template went
|
|
939
|
+
* missing rather than to how many findings were printed.
|
|
940
|
+
*/
|
|
941
|
+
export declare class TypescriptTreeService {
|
|
942
|
+
private readonly scoringService;
|
|
943
|
+
private readonly typeScriptNodesService;
|
|
944
|
+
constructor(scoringService: ScoringService, typeScriptNodesService: TypescriptNodesService);
|
|
945
|
+
/** Describes a template node with no instance counterpart. */
|
|
946
|
+
private buildError;
|
|
947
|
+
/**
|
|
948
|
+
* Descends into whichever candidate explains the template best.
|
|
949
|
+
*
|
|
950
|
+
* Several instance nodes can share a key or kind — two methods with the same
|
|
951
|
+
* name on different classes, say — so the one leaving the least of the
|
|
952
|
+
* template unaccounted for is taken as the intended match.
|
|
953
|
+
*
|
|
954
|
+
* Weighed by failed weight rather than by error count: one finding standing
|
|
955
|
+
* in for a whole missing class is a worse match than two missing imports,
|
|
956
|
+
* and counting findings would have picked the wrong one.
|
|
957
|
+
*/
|
|
958
|
+
private compareBestCandidate;
|
|
959
|
+
/** Matches one template child against the instance's children. */
|
|
960
|
+
private compareChild;
|
|
961
|
+
/** Compares one level of two trees, descending into every match. */
|
|
962
|
+
compareTree(args: CompareTreeArguments): TreeComparison;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
/**
|
|
966
|
+
* Anything that carries a weight.
|
|
967
|
+
*
|
|
968
|
+
* Deliberately narrower than `ConformetryDifference`: a language package weighs its
|
|
969
|
+
* own internal findings before they are ever described as conformetry differences,
|
|
970
|
+
* and requiring the full error shape would force it to build messages just to
|
|
971
|
+
* count.
|
|
972
|
+
*/
|
|
973
|
+
export declare interface WeightedFinding {
|
|
974
|
+
readonly weight?: number;
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
export { }
|