gurkencheck 0.0.7 → 0.0.9
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/README.md +19 -0
- package/dist/config-parser.d.ts.map +1 -1
- package/dist/config-parser.js +7 -1
- package/dist/config-parser.js.map +1 -1
- package/dist/diagnostics.d.ts +34 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +53 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/feature-finder.d.ts +38 -0
- package/dist/feature-finder.d.ts.map +1 -1
- package/dist/feature-finder.js +86 -8
- package/dist/feature-finder.js.map +1 -1
- package/dist/formatters/index.d.ts +44 -4
- package/dist/formatters/index.d.ts.map +1 -1
- package/dist/formatters/index.js +52 -15
- package/dist/formatters/index.js.map +1 -1
- package/dist/formatters/stylish.d.ts +3 -0
- package/dist/formatters/stylish.d.ts.map +1 -1
- package/dist/formatters/stylish.js +32 -1
- package/dist/formatters/stylish.js.map +1 -1
- package/dist/formatters/tap.d.ts +14 -0
- package/dist/formatters/tap.d.ts.map +1 -1
- package/dist/formatters/tap.js +22 -7
- package/dist/formatters/tap.js.map +1 -1
- package/dist/gherkin/parse.d.ts +42 -1
- package/dist/gherkin/parse.d.ts.map +1 -1
- package/dist/gherkin/parse.js +68 -3
- package/dist/gherkin/parse.js.map +1 -1
- package/dist/index.d.ts +18 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -6
- package/dist/index.js.map +1 -1
- package/dist/linter.d.ts +31 -3
- package/dist/linter.d.ts.map +1 -1
- package/dist/linter.js +82 -15
- package/dist/linter.js.map +1 -1
- package/dist/logger.d.ts +2 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +4 -0
- package/dist/logger.js.map +1 -1
- package/dist/main.d.ts +9 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +147 -26
- package/dist/main.js.map +1 -1
- package/dist/rules/no-dupe-feature-names.d.ts.map +1 -1
- package/dist/rules/no-dupe-feature-names.js +26 -13
- package/dist/rules/no-dupe-feature-names.js.map +1 -1
- package/dist/rules/no-dupe-file-names.d.ts.map +1 -1
- package/dist/rules/no-dupe-file-names.js +32 -17
- package/dist/rules/no-dupe-file-names.js.map +1 -1
- package/dist/rules/no-dupe-scenario-names.d.ts.map +1 -1
- package/dist/rules/no-dupe-scenario-names.js +34 -22
- package/dist/rules/no-dupe-scenario-names.js.map +1 -1
- package/dist/rules.d.ts +41 -3
- package/dist/rules.d.ts.map +1 -1
- package/dist/rules.js +112 -4
- package/dist/rules.js.map +1 -1
- package/dist/stats/collect.d.ts +8 -0
- package/dist/stats/collect.d.ts.map +1 -1
- package/dist/stats/collect.js +67 -37
- package/dist/stats/collect.js.map +1 -1
- package/dist/stats/command.d.ts +2 -1
- package/dist/stats/command.d.ts.map +1 -1
- package/dist/stats/command.js +34 -34
- package/dist/stats/command.js.map +1 -1
- package/dist/types.d.ts +47 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/util/glob.d.ts +18 -0
- package/dist/util/glob.d.ts.map +1 -1
- package/dist/util/glob.js +126 -22
- package/dist/util/glob.js.map +1 -1
- package/dist/util/stream.d.ts +31 -0
- package/dist/util/stream.d.ts.map +1 -0
- package/dist/util/stream.js +67 -0
- package/dist/util/stream.js.map +1 -0
- package/dist/watch.d.ts +36 -0
- package/dist/watch.d.ts.map +1 -0
- package/dist/watch.js +203 -0
- package/dist/watch.js.map +1 -0
- package/package.json +1 -1
package/dist/main.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import type { Diagnostics } from './diagnostics.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Runs the command line.
|
|
5
|
+
*
|
|
6
|
+
* Anything said about the run goes through `diagnostics`, which says nothing
|
|
7
|
+
* unless it is given somewhere to say it - so calling this from a test or a
|
|
8
|
+
* library leaves the console alone until you ask for it.
|
|
9
|
+
*/
|
|
10
|
+
export declare function run(argv: readonly string[], diagnostics?: Diagnostics): Promise<number>;
|
|
3
11
|
//# sourceMappingURL=main.d.ts.map
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AAwBA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAsClD;;;;;;GAMG;AACH,wBAAsB,GAAG,CACvB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,WAAW,GAAE,WAAoB,GAChC,OAAO,CAAC,MAAM,CAAC,CAmKjB"}
|
package/dist/main.js
CHANGED
|
@@ -7,14 +7,17 @@ import { pathToFileURL } from 'node:url';
|
|
|
7
7
|
import { parseArgs } from 'node:util';
|
|
8
8
|
import { DEFAULT_CONFIG_FILE_NAME, readConfiguration } from './config-parser.js';
|
|
9
9
|
import { EXIT_LINT_ERRORS, EXIT_OK, EXIT_USAGE } from './exit-codes.js';
|
|
10
|
-
import { DEFAULT_IGNORE_FILE_NAME,
|
|
11
|
-
import {
|
|
10
|
+
import { DEFAULT_IGNORE_FILE_NAME, featureRoots, findFeatureFileStream, } from './feature-finder.js';
|
|
11
|
+
import { watch } from './watch.js';
|
|
12
|
+
import { DEFAULT_FORMAT, FORMATTERS, loadFormatter, loadStreamingFormatter, } from './formatters/index.js';
|
|
12
13
|
import { isKnownLanguage } from './gherkin/dialects.js';
|
|
13
|
-
import { hasErrors, lint } from './linter.js';
|
|
14
|
-
import
|
|
14
|
+
import { hasErrors, lint, lintStream } from './linter.js';
|
|
15
|
+
import { SILENT, TO_STDERR } from './diagnostics.js';
|
|
15
16
|
import { loadRules } from './rules.js';
|
|
16
17
|
import { runStats } from './stats/command.js';
|
|
17
18
|
import { version } from './version.js';
|
|
19
|
+
/** Directory names a watch takes no notice of, however deep they turn up. */
|
|
20
|
+
const DEFAULT_IGNORED_NAMES = ['node_modules', '.git'];
|
|
18
21
|
function usage() {
|
|
19
22
|
return [
|
|
20
23
|
'Usage: gurkencheck [options] <feature-files>',
|
|
@@ -29,6 +32,7 @@ function usage() {
|
|
|
29
32
|
` -i, --ignore <globs> comma separated globs to skip, overriding ${DEFAULT_IGNORE_FILE_NAME}`,
|
|
30
33
|
' -r, --rulesdir <dir> directory of custom rules; may be given more than once',
|
|
31
34
|
' -l, --language <code> dialect for files with no "# language:" header',
|
|
35
|
+
' -w, --watch keep running, checking again whenever a file changes',
|
|
32
36
|
' -h, --help show this message',
|
|
33
37
|
' -v, --version show the version number',
|
|
34
38
|
'',
|
|
@@ -40,12 +44,19 @@ function usage() {
|
|
|
40
44
|
'Documentation: https://sebs.github.io/gurkencheck/',
|
|
41
45
|
].join('\n');
|
|
42
46
|
}
|
|
43
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Runs the command line.
|
|
49
|
+
*
|
|
50
|
+
* Anything said about the run goes through `diagnostics`, which says nothing
|
|
51
|
+
* unless it is given somewhere to say it - so calling this from a test or a
|
|
52
|
+
* library leaves the console alone until you ask for it.
|
|
53
|
+
*/
|
|
54
|
+
export async function run(argv, diagnostics = SILENT) {
|
|
44
55
|
// Subcommands are matched before the options are read, so that `stats` can
|
|
45
56
|
// accept a --format of its own without the two sets of names colliding.
|
|
46
57
|
// A directory really called `stats` is still lintable, as `./stats`.
|
|
47
58
|
if (argv[0] === 'stats') {
|
|
48
|
-
return runStats(argv.slice(1));
|
|
59
|
+
return runStats(argv.slice(1), diagnostics);
|
|
49
60
|
}
|
|
50
61
|
let parsed;
|
|
51
62
|
try {
|
|
@@ -57,6 +68,7 @@ export async function run(argv) {
|
|
|
57
68
|
ignore: { type: 'string', short: 'i' },
|
|
58
69
|
rulesdir: { type: 'string', short: 'r', multiple: true },
|
|
59
70
|
language: { type: 'string', short: 'l' },
|
|
71
|
+
watch: { type: 'boolean', short: 'w' },
|
|
60
72
|
help: { type: 'boolean', short: 'h' },
|
|
61
73
|
version: { type: 'boolean', short: 'v' },
|
|
62
74
|
},
|
|
@@ -64,8 +76,11 @@ export async function run(argv) {
|
|
|
64
76
|
});
|
|
65
77
|
}
|
|
66
78
|
catch (thrown) {
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
diagnostics.report({
|
|
80
|
+
level: 'error',
|
|
81
|
+
message: thrown instanceof Error ? thrown.message : String(thrown),
|
|
82
|
+
});
|
|
83
|
+
diagnostics.report({ level: 'notice', message: usage() });
|
|
69
84
|
return EXIT_USAGE;
|
|
70
85
|
}
|
|
71
86
|
const { values, positionals } = parsed;
|
|
@@ -83,46 +98,128 @@ export async function run(argv) {
|
|
|
83
98
|
rules = await loadRules(additionalRulesDirs);
|
|
84
99
|
}
|
|
85
100
|
catch (thrown) {
|
|
86
|
-
|
|
101
|
+
diagnostics.report({
|
|
102
|
+
level: 'error',
|
|
103
|
+
message: thrown instanceof Error ? thrown.message : String(thrown),
|
|
104
|
+
});
|
|
87
105
|
return EXIT_USAGE;
|
|
88
106
|
}
|
|
89
107
|
const configuration = await readConfiguration(values.config, rules);
|
|
90
108
|
if (!configuration.ok) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
109
|
+
diagnostics.report({
|
|
110
|
+
level: 'error',
|
|
111
|
+
message: configuration.message,
|
|
112
|
+
details: configuration.details,
|
|
113
|
+
});
|
|
95
114
|
return EXIT_USAGE;
|
|
96
115
|
}
|
|
97
116
|
const ignore = values.ignore?.split(',').map((pattern) => pattern.trim());
|
|
98
|
-
|
|
117
|
+
// Files are handed over as the walk finds them, so reading and checking
|
|
118
|
+
// start on the first one rather than after the last. Only the patterns are
|
|
119
|
+
// checked here; the walk itself happens once per pass.
|
|
120
|
+
const { invalidPatterns } = findFeatureFileStream(positionals, ignore);
|
|
99
121
|
if (invalidPatterns.length > 0) {
|
|
100
122
|
for (const pattern of invalidPatterns) {
|
|
101
|
-
|
|
123
|
+
diagnostics.report({
|
|
124
|
+
level: 'error',
|
|
125
|
+
message: `Invalid format of the feature file path/pattern: "${pattern}".`,
|
|
126
|
+
});
|
|
102
127
|
}
|
|
103
|
-
|
|
128
|
+
diagnostics.report({
|
|
129
|
+
level: 'detail',
|
|
130
|
+
message: 'To run the linter please specify an existing feature file, directory or glob.',
|
|
131
|
+
});
|
|
104
132
|
return EXIT_USAGE;
|
|
105
133
|
}
|
|
134
|
+
// A format that can write as the run goes on is driven from the stream, so
|
|
135
|
+
// the first findings appear while the rest of the files are still being
|
|
136
|
+
// checked. The others are handed the whole run at the end, as before.
|
|
106
137
|
let formatter;
|
|
138
|
+
let streaming;
|
|
107
139
|
try {
|
|
108
|
-
|
|
140
|
+
streaming = await loadStreamingFormatter(values.format);
|
|
141
|
+
if (streaming === undefined) {
|
|
142
|
+
formatter = await loadFormatter(values.format);
|
|
143
|
+
}
|
|
109
144
|
}
|
|
110
145
|
catch (thrown) {
|
|
111
|
-
|
|
146
|
+
diagnostics.report({
|
|
147
|
+
level: 'error',
|
|
148
|
+
message: thrown instanceof Error ? thrown.message : String(thrown),
|
|
149
|
+
});
|
|
112
150
|
return EXIT_USAGE;
|
|
113
151
|
}
|
|
114
152
|
const language = values.language ?? configuration.language;
|
|
115
153
|
if (language !== undefined && !isKnownLanguage(language)) {
|
|
116
|
-
|
|
154
|
+
diagnostics.report({
|
|
155
|
+
level: 'error',
|
|
156
|
+
message: `Unknown language "${language}". Use a Gherkin language code, such as "fr".`,
|
|
157
|
+
});
|
|
117
158
|
return EXIT_USAGE;
|
|
118
159
|
}
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
160
|
+
// Discovery is a stream, and a stream can only be read once - so a run that
|
|
161
|
+
// may happen more than once is given a fresh one each time.
|
|
162
|
+
const discover = () => findFeatureFileStream(positionals, ignore).files;
|
|
163
|
+
const checkOnce = async () => {
|
|
164
|
+
if (streaming !== undefined) {
|
|
165
|
+
return await runStreaming(streaming, discover(), configuration.configuration, rules, language, diagnostics);
|
|
166
|
+
}
|
|
167
|
+
const results = await lint(discover(), configuration.configuration, rules, { language });
|
|
168
|
+
// A formatter may print the output itself or hand it back as a string.
|
|
169
|
+
let output;
|
|
170
|
+
try {
|
|
171
|
+
output = await formatter(results);
|
|
172
|
+
}
|
|
173
|
+
catch (thrown) {
|
|
174
|
+
diagnostics.report({
|
|
175
|
+
level: 'error',
|
|
176
|
+
message: `The formatter failed: ${thrown instanceof Error ? thrown.message : String(thrown)}`,
|
|
177
|
+
});
|
|
178
|
+
return EXIT_USAGE;
|
|
179
|
+
}
|
|
180
|
+
if (typeof output === 'string' && output !== '') {
|
|
181
|
+
console.log(output);
|
|
182
|
+
}
|
|
183
|
+
return hasErrors(results) ? EXIT_LINT_ERRORS : EXIT_OK;
|
|
184
|
+
};
|
|
185
|
+
if (values.watch === true) {
|
|
186
|
+
// Watching never fails: a run that found something is the normal state of
|
|
187
|
+
// affairs while you are fixing it, and the exit code is only read once,
|
|
188
|
+
// when you stop.
|
|
189
|
+
return await watch(featureRoots(positionals), values.config ?? DEFAULT_CONFIG_FILE_NAME, { diagnostics, ignore: DEFAULT_IGNORED_NAMES }, checkOnce);
|
|
190
|
+
}
|
|
191
|
+
return await checkOnce();
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Lints while writing, so a long run says something before it is over.
|
|
195
|
+
*
|
|
196
|
+
* Nothing is kept but whether anything has failed: holding every result to
|
|
197
|
+
* count them at the end would give back the memory the stream just saved.
|
|
198
|
+
*/
|
|
199
|
+
async function runStreaming(streaming, files, configuration, rules, language, diagnostics) {
|
|
200
|
+
const write = (text) => {
|
|
201
|
+
if (text !== undefined && text !== '') {
|
|
202
|
+
process.stdout.write(text);
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
let failed = false;
|
|
206
|
+
try {
|
|
207
|
+
const run = streaming();
|
|
208
|
+
write(run.start?.());
|
|
209
|
+
for await (const result of lintStream(files, configuration, rules, { language })) {
|
|
210
|
+
failed ||= result.errors.some((error) => (error.severity ?? 'error') === 'error');
|
|
211
|
+
write(run.file(result));
|
|
212
|
+
}
|
|
213
|
+
write(run.end?.());
|
|
214
|
+
}
|
|
215
|
+
catch (thrown) {
|
|
216
|
+
diagnostics.report({
|
|
217
|
+
level: 'error',
|
|
218
|
+
message: `The formatter failed: ${thrown instanceof Error ? thrown.message : String(thrown)}`,
|
|
219
|
+
});
|
|
220
|
+
return EXIT_USAGE;
|
|
124
221
|
}
|
|
125
|
-
return
|
|
222
|
+
return failed ? EXIT_LINT_ERRORS : EXIT_OK;
|
|
126
223
|
}
|
|
127
224
|
/**
|
|
128
225
|
* True when this file is the script node was asked to run.
|
|
@@ -143,8 +240,32 @@ function isMainModule() {
|
|
|
143
240
|
return false;
|
|
144
241
|
}
|
|
145
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* Runs the command line, turning anything unexpected into a usage failure.
|
|
245
|
+
*
|
|
246
|
+
* Without this, a crash leaves node to exit 1 - which is the code for "the
|
|
247
|
+
* linter found something" - so a broken run and a dirty feature file would
|
|
248
|
+
* look identical to CI. Anything reaching this handler is a bug rather than a
|
|
249
|
+
* finding, so the whole error is printed and the run fails as unusable.
|
|
250
|
+
*/
|
|
251
|
+
async function main(argv) {
|
|
252
|
+
try {
|
|
253
|
+
return await run(argv, TO_STDERR);
|
|
254
|
+
}
|
|
255
|
+
catch (thrown) {
|
|
256
|
+
TO_STDERR.report({
|
|
257
|
+
level: 'error',
|
|
258
|
+
message: 'gurkencheck stopped unexpectedly. This is a bug, please report it:',
|
|
259
|
+
});
|
|
260
|
+
TO_STDERR.report({
|
|
261
|
+
level: 'detail',
|
|
262
|
+
message: thrown instanceof Error ? (thrown.stack ?? thrown.message) : String(thrown),
|
|
263
|
+
});
|
|
264
|
+
return EXIT_USAGE;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
146
267
|
// Only take over the process when run as a command, not when imported.
|
|
147
268
|
if (isMainModule()) {
|
|
148
|
-
process.exitCode = await
|
|
269
|
+
process.exitCode = await main(process.argv.slice(2));
|
|
149
270
|
}
|
|
150
271
|
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA;;GAEG;AACH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAC,aAAa,EAAC,MAAM,UAAU,CAAC;AACvC,OAAO,EAAC,SAAS,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,EAAC,wBAAwB,EAAE,iBAAiB,EAAC,MAAM,oBAAoB,CAAC;AAC/E,OAAO,EAAC,gBAAgB,EAAE,OAAO,EAAE,UAAU,EAAC,MAAM,iBAAiB,CAAC;AACtE,OAAO,
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-dupe-feature-names.d.ts","sourceRoot":"","sources":["../../src/rules/no-dupe-feature-names.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,
|
|
1
|
+
{"version":3,"file":"no-dupe-feature-names.d.ts","sourceRoot":"","sources":["../../src/rules/no-dupe-feature-names.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,QAAQ,EAAa,MAAM,aAAa,CAAC;AActD,QAAA,MAAM,IAAI,EAAE,QAqCX,CAAC;eAEa,IAAI"}
|
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
import { at } from '../util/location.js';
|
|
2
2
|
const name = 'no-dupe-feature-names';
|
|
3
|
-
/** Feature name -> the files it has already been seen in. */
|
|
4
|
-
const seen = new Map();
|
|
5
3
|
const rule = {
|
|
6
4
|
name,
|
|
7
|
-
|
|
8
|
-
seen.clear();
|
|
9
|
-
},
|
|
10
|
-
run(feature, file) {
|
|
5
|
+
run(feature, file, _configuration, context) {
|
|
11
6
|
if (feature === undefined) {
|
|
12
7
|
return [];
|
|
13
8
|
}
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
const seen = context.state(() => new Map());
|
|
10
|
+
const sightings = seen.get(feature.name) ?? [];
|
|
11
|
+
sightings.push({ filePath: file.relativePath, at: at(feature.location) });
|
|
12
|
+
seen.set(feature.name, sightings);
|
|
13
|
+
return [];
|
|
14
|
+
},
|
|
15
|
+
onRunEnd(_configuration, context) {
|
|
16
|
+
const seen = context.state(() => new Map());
|
|
17
|
+
const findings = [];
|
|
18
|
+
for (const sightings of seen.values()) {
|
|
19
|
+
if (sightings.length < 2) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
// Each file hears about the others. Reporting only the later one made
|
|
23
|
+
// the finding move when the files were given in a different order.
|
|
24
|
+
for (const sighting of sightings) {
|
|
25
|
+
const others = sightings.filter((other) => other !== sighting);
|
|
26
|
+
findings.push({
|
|
27
|
+
message: `Feature name is also used in: ${others.map((other) => other.filePath).join(', ')}`,
|
|
28
|
+
rule: name,
|
|
29
|
+
...sighting.at,
|
|
30
|
+
filePath: sighting.filePath,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
18
33
|
}
|
|
19
|
-
|
|
20
|
-
previous.push(file.relativePath);
|
|
21
|
-
return [{ message, rule: name, ...at(feature.location) }];
|
|
34
|
+
return findings;
|
|
22
35
|
},
|
|
23
36
|
};
|
|
24
37
|
export default rule;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-dupe-feature-names.js","sourceRoot":"","sources":["../../src/rules/no-dupe-feature-names.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,EAAE,EAAC,MAAM,qBAAqB,CAAC;AAEvC,MAAM,IAAI,GAAG,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"no-dupe-feature-names.js","sourceRoot":"","sources":["../../src/rules/no-dupe-feature-names.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,EAAE,EAAC,MAAM,qBAAqB,CAAC;AAEvC,MAAM,IAAI,GAAG,uBAAuB,CAAC;AAWrC,MAAM,IAAI,GAAa;IACrB,IAAI;IACJ,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO;QACxC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAO,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;QAClD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAC/C,SAAS,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAC,CAAC,CAAC;QACxE,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAElC,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,QAAQ,CAAC,cAAc,EAAE,OAAO;QAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAO,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAiB,EAAE,CAAC;QAElC,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACtC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzB,SAAS;YACX,CAAC;YACD,sEAAsE;YACtE,mEAAmE;YACnE,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;gBACjC,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC;gBAC/D,QAAQ,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,iCAAiC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBAC5F,IAAI,EAAE,IAAI;oBACV,GAAG,QAAQ,CAAC,EAAE;oBACd,QAAQ,EAAE,QAAQ,CAAC,QAAQ;iBAC5B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-dupe-file-names.d.ts","sourceRoot":"","sources":["../../src/rules/no-dupe-file-names.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,QAAQ,
|
|
1
|
+
{"version":3,"file":"no-dupe-file-names.d.ts","sourceRoot":"","sources":["../../src/rules/no-dupe-file-names.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,QAAQ,EAAa,MAAM,aAAa,CAAC;AAatD,QAAA,MAAM,IAAI,EAAE,QAyCX,CAAC;eAEa,IAAI"}
|
|
@@ -1,27 +1,42 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
const name = 'no-dupe-file-names';
|
|
3
|
-
/**
|
|
4
|
-
* Two feature files with the same name in different folders are easy to
|
|
5
|
-
* confuse, and tools that write one report per feature - naming it after the
|
|
6
|
-
* file - quietly overwrite one with the other.
|
|
7
|
-
*/
|
|
8
|
-
/** File name without its extension -> the paths it has already been seen at. */
|
|
9
|
-
const seen = new Map();
|
|
10
3
|
const rule = {
|
|
11
4
|
name,
|
|
12
|
-
|
|
13
|
-
seen.
|
|
14
|
-
},
|
|
15
|
-
run(_feature, file) {
|
|
5
|
+
run(_feature, file, _configuration, context) {
|
|
6
|
+
const seen = context.state(() => new Map());
|
|
16
7
|
const fileName = path.basename(file.relativePath, path.extname(file.relativePath));
|
|
17
|
-
const
|
|
18
|
-
if (
|
|
8
|
+
const paths = seen.get(fileName);
|
|
9
|
+
if (paths === undefined) {
|
|
19
10
|
seen.set(fileName, [file.relativePath]);
|
|
20
|
-
return [];
|
|
21
11
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
12
|
+
else if (!paths.includes(file.relativePath)) {
|
|
13
|
+
// A file handed to the linter twice is the caller repeating itself, not
|
|
14
|
+
// two files sharing a name.
|
|
15
|
+
paths.push(file.relativePath);
|
|
16
|
+
}
|
|
17
|
+
return [];
|
|
18
|
+
},
|
|
19
|
+
onRunEnd(_configuration, context) {
|
|
20
|
+
const seen = context.state(() => new Map());
|
|
21
|
+
const findings = [];
|
|
22
|
+
for (const paths of seen.values()) {
|
|
23
|
+
if (paths.length < 2) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
// Every file involved hears about every other. Which of them was read
|
|
27
|
+
// first is an accident of the order the files came in, so blaming that
|
|
28
|
+
// one alone says more about the run than about the files.
|
|
29
|
+
for (const filePath of paths) {
|
|
30
|
+
const others = paths.filter((other) => other !== filePath);
|
|
31
|
+
findings.push({
|
|
32
|
+
message: `File name is also used in: ${others.join(', ')}`,
|
|
33
|
+
rule: name,
|
|
34
|
+
line: 0,
|
|
35
|
+
filePath,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return findings;
|
|
25
40
|
},
|
|
26
41
|
};
|
|
27
42
|
export default rule;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-dupe-file-names.js","sourceRoot":"","sources":["../../src/rules/no-dupe-file-names.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,IAAI,GAAG,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"no-dupe-file-names.js","sourceRoot":"","sources":["../../src/rules/no-dupe-file-names.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,IAAI,GAAG,oBAAoB,CAAC;AAWlC,MAAM,IAAI,GAAa;IACrB,IAAI;IACJ,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO;QACzC,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAO,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;QACnF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAEjC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;QAC1C,CAAC;aAAM,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YAC9C,wEAAwE;YACxE,4BAA4B;YAC5B,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAChC,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,QAAQ,CAAC,cAAc,EAAE,OAAO;QAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAO,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAiB,EAAE,CAAC;QAElC,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YAClC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACrB,SAAS;YACX,CAAC;YACD,sEAAsE;YACtE,uEAAuE;YACvE,0DAA0D;YAC1D,KAAK,MAAM,QAAQ,IAAI,KAAK,EAAE,CAAC;gBAC7B,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC;gBAC3D,QAAQ,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,8BAA8B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE;oBAC1D,IAAI,EAAE,IAAI;oBACV,IAAI,EAAE,CAAC;oBACP,QAAQ;iBACT,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-dupe-scenario-names.d.ts","sourceRoot":"","sources":["../../src/rules/no-dupe-scenario-names.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,QAAQ,
|
|
1
|
+
{"version":3,"file":"no-dupe-scenario-names.d.ts","sourceRoot":"","sources":["../../src/rules/no-dupe-scenario-names.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,QAAQ,EAAa,MAAM,aAAa,CAAC;AAiBtD,QAAA,MAAM,IAAI,EAAE,QAkDX,CAAC;eAEa,IAAI"}
|
|
@@ -3,38 +3,50 @@ import { at } from '../util/location.js';
|
|
|
3
3
|
const name = 'no-dupe-scenario-names';
|
|
4
4
|
/** Whether duplicates are looked for across all files or within each file. */
|
|
5
5
|
const availableConfigs = ['anywhere', 'in-feature'];
|
|
6
|
-
/** Scenario name -> everywhere it has already been seen. */
|
|
7
|
-
const seen = new Map();
|
|
8
6
|
const rule = {
|
|
9
7
|
name,
|
|
10
8
|
availableConfigs,
|
|
11
|
-
|
|
12
|
-
seen.clear();
|
|
13
|
-
},
|
|
14
|
-
run(feature, file, configuration) {
|
|
9
|
+
run(feature, file, configuration, context) {
|
|
15
10
|
if (feature === undefined) {
|
|
16
11
|
return [];
|
|
17
12
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
const errors = [];
|
|
13
|
+
const seen = context.state(() => new Map());
|
|
14
|
+
const withinOneFile = configuration === 'in-feature';
|
|
22
15
|
for (const { scenario } of scenariosOf(feature)) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
// Told to look within each file, a name only clashes with the same name
|
|
17
|
+
// in the same file - so which file it is in is part of what makes a
|
|
18
|
+
// name that name. A newline cannot appear in either half, so the two
|
|
19
|
+
// cannot run together into a key that means something else.
|
|
20
|
+
const key = withinOneFile ? `${file.relativePath}\n${scenario.name}` : scenario.name;
|
|
21
|
+
const sightings = seen.get(key) ?? [];
|
|
22
|
+
sightings.push({ filePath: file.relativePath, at: at(scenario.location) });
|
|
23
|
+
seen.set(key, sightings);
|
|
24
|
+
}
|
|
25
|
+
return [];
|
|
26
|
+
},
|
|
27
|
+
onRunEnd(_configuration, context) {
|
|
28
|
+
const seen = context.state(() => new Map());
|
|
29
|
+
const findings = [];
|
|
30
|
+
for (const sightings of seen.values()) {
|
|
31
|
+
if (sightings.length < 2) {
|
|
27
32
|
continue;
|
|
28
33
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// Every scenario in the group hears about the others, so renaming can
|
|
35
|
+
// start from whichever one is easiest to change.
|
|
36
|
+
for (const sighting of sightings) {
|
|
37
|
+
const others = sightings.filter((other) => other !== sighting);
|
|
38
|
+
const where = others
|
|
39
|
+
.map((other) => `${other.filePath}:${other.at.line}`)
|
|
40
|
+
.join(', ');
|
|
41
|
+
findings.push({
|
|
42
|
+
message: `Scenario name is also used in: ${where}`,
|
|
43
|
+
rule: name,
|
|
44
|
+
...sighting.at,
|
|
45
|
+
filePath: sighting.filePath,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
36
48
|
}
|
|
37
|
-
return
|
|
49
|
+
return findings;
|
|
38
50
|
},
|
|
39
51
|
};
|
|
40
52
|
export default rule;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-dupe-scenario-names.js","sourceRoot":"","sources":["../../src/rules/no-dupe-scenario-names.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,wBAAwB,CAAC;AAEnD,OAAO,EAAC,EAAE,EAAC,MAAM,qBAAqB,CAAC;AAEvC,MAAM,IAAI,GAAG,wBAAwB,CAAC;AAEtC,8EAA8E;AAC9E,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,YAAY,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"no-dupe-scenario-names.js","sourceRoot":"","sources":["../../src/rules/no-dupe-scenario-names.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,wBAAwB,CAAC;AAEnD,OAAO,EAAC,EAAE,EAAC,MAAM,qBAAqB,CAAC;AAEvC,MAAM,IAAI,GAAG,wBAAwB,CAAC;AAEtC,8EAA8E;AAC9E,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,YAAY,CAAU,CAAC;AAW7D,MAAM,IAAI,GAAa;IACrB,IAAI;IACJ,gBAAgB;IAChB,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO;QACvC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAO,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;QAClD,MAAM,aAAa,GAAG,aAAa,KAAK,YAAY,CAAC;QAErD,KAAK,MAAM,EAAC,QAAQ,EAAC,IAAI,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9C,wEAAwE;YACxE,oEAAoE;YACpE,qEAAqE;YACrE,4DAA4D;YAC5D,MAAM,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY,KAAK,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;YACrF,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;YACtC,SAAS,CAAC,IAAI,CAAC,EAAC,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAC,CAAC,CAAC;YACzE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC3B,CAAC;QAED,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,QAAQ,CAAC,cAAc,EAAE,OAAO;QAC9B,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAO,GAAG,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAiB,EAAE,CAAC;QAElC,KAAK,MAAM,SAAS,IAAI,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACtC,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzB,SAAS;YACX,CAAC;YACD,sEAAsE;YACtE,iDAAiD;YACjD,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;gBACjC,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC;gBAC/D,MAAM,KAAK,GAAG,MAAM;qBACjB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;qBACpD,IAAI,CAAC,IAAI,CAAC,CAAC;gBACd,QAAQ,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,kCAAkC,KAAK,EAAE;oBAClD,IAAI,EAAE,IAAI;oBACV,GAAG,QAAQ,CAAC,EAAE;oBACd,QAAQ,EAAE,QAAQ,CAAC,QAAQ;iBAC5B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
package/dist/rules.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Configuration, FeatureFile, RuleConfig, RuleError, RuleRegistry, Severity } from './types.ts';
|
|
1
|
+
import type { Configuration, FeatureFile, RuleConfig, RuleError, RuleRegistry, RunContext, RunFinding, Severity } from './types.ts';
|
|
2
2
|
import type { Feature } from '@cucumber/messages';
|
|
3
3
|
/**
|
|
4
4
|
* The built-in rules, plus any found in the given directories. A custom rule
|
|
@@ -11,14 +11,52 @@ export declare function isRuleEnabled(config: RuleConfig | undefined): boolean;
|
|
|
11
11
|
export declare function getRuleSeverity(config: RuleConfig | undefined): Severity;
|
|
12
12
|
/** The rule's own settings, or `undefined` when only a state was given. */
|
|
13
13
|
export declare function getRuleSettings(config: RuleConfig | undefined): unknown;
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* Clears the state kept by rules that look for duplicates across files.
|
|
16
|
+
*
|
|
17
|
+
* @deprecated Use `beginRun`, which does this and hands each rule a context
|
|
18
|
+
* of its own besides.
|
|
19
|
+
*/
|
|
15
20
|
export declare function resetRules(rules: RuleRegistry): void;
|
|
21
|
+
/**
|
|
22
|
+
* One rule's state for one run, made the first time the rule asks for it.
|
|
23
|
+
*
|
|
24
|
+
* Exported because testing a rule on its own means handing it one of these.
|
|
25
|
+
*/
|
|
26
|
+
export declare function newRunContext(): RunContext;
|
|
27
|
+
/**
|
|
28
|
+
* What one run of the rules keeps for itself.
|
|
29
|
+
*
|
|
30
|
+
* Two runs in the same process have two of these, so a rule remembering what
|
|
31
|
+
* it has seen cannot see what another run has seen.
|
|
32
|
+
*/
|
|
33
|
+
export interface Run {
|
|
34
|
+
/** The context for one rule, made the first time it is asked for. */
|
|
35
|
+
contextFor(ruleName: string): RunContext;
|
|
36
|
+
}
|
|
37
|
+
/** Starts a run, telling every enabled rule that one is beginning. */
|
|
38
|
+
export declare function beginRun(rules: RuleRegistry, configuration: Configuration): Run;
|
|
39
|
+
/**
|
|
40
|
+
* Ends a run, collecting what could only be worked out once every file had
|
|
41
|
+
* been seen.
|
|
42
|
+
*
|
|
43
|
+
* As while checking a file, a rule that throws costs you its own findings
|
|
44
|
+
* rather than everyone else's.
|
|
45
|
+
*/
|
|
46
|
+
export declare function finishRun(rules: RuleRegistry, configuration: Configuration, run: Run): Promise<RunFinding[]>;
|
|
16
47
|
/**
|
|
17
48
|
* Runs every enabled rule against one feature file.
|
|
18
49
|
*
|
|
19
50
|
* Rules are awaited one at a time. Most return an array outright, in which
|
|
20
51
|
* case awaiting costs nothing; a rule that needs to wait for something can
|
|
21
52
|
* return a promise instead.
|
|
53
|
+
*
|
|
54
|
+
* A rule that throws is reported as an `unexpected-error` finding naming it,
|
|
55
|
+
* and the remaining rules still run.
|
|
56
|
+
*
|
|
57
|
+
* `run` carries the state the rules keep between the files of one run. Left
|
|
58
|
+
* out, a run of this one file alone is started, which is what a caller
|
|
59
|
+
* checking a single file wants.
|
|
22
60
|
*/
|
|
23
|
-
export declare function runEnabledRules(feature: Feature | undefined, file: FeatureFile, configuration: Configuration, rules: RuleRegistry): Promise<RuleError[]>;
|
|
61
|
+
export declare function runEnabledRules(feature: Feature | undefined, file: FeatureFile, configuration: Configuration, rules: RuleRegistry, run?: Run): Promise<RuleError[]>;
|
|
24
62
|
//# sourceMappingURL=rules.d.ts.map
|
package/dist/rules.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EAEX,UAAU,EACV,SAAS,EACT,YAAY,EACZ,QAAQ,EACT,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACV,aAAa,EACb,WAAW,EAEX,UAAU,EACV,SAAS,EACT,YAAY,EACZ,UAAU,EACV,UAAU,EACV,QAAQ,EACT,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAmDhD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,mBAAmB,GAAE,SAAS,MAAM,EAAO,GAC1C,OAAO,CAAC,YAAY,CAAC,CAavB;AAOD,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAGrE;AAED,iFAAiF;AACjF,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,CAExE;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,CAEvE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,IAAI,CAIpD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,UAAU,CAY1C;AAED;;;;;GAKG;AACH,MAAM,WAAW,GAAG;IAClB,qEAAqE;IACrE,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC;CAC1C;AAED,sEAAsE;AACtE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,GAAG,GAAG,CAyB/E;AAED;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,aAAa,EAC5B,GAAG,EAAE,GAAG,GACP,OAAO,CAAC,UAAU,EAAE,CAAC,CA0BvB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,OAAO,GAAG,SAAS,EAC5B,IAAI,EAAE,WAAW,EACjB,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,YAAY,EACnB,GAAG,GAAE,GAAoC,GACxC,OAAO,CAAC,SAAS,EAAE,CAAC,CAuCtB"}
|