gurkencheck 0.0.6 → 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.
Files changed (120) hide show
  1. package/README.md +134 -3
  2. package/dist/config-parser.d.ts.map +1 -1
  3. package/dist/config-parser.js +7 -1
  4. package/dist/config-parser.js.map +1 -1
  5. package/dist/diagnostics.d.ts +34 -0
  6. package/dist/diagnostics.d.ts.map +1 -0
  7. package/dist/diagnostics.js +53 -0
  8. package/dist/diagnostics.js.map +1 -0
  9. package/dist/exit-codes.d.ts +10 -0
  10. package/dist/exit-codes.d.ts.map +1 -0
  11. package/dist/exit-codes.js +10 -0
  12. package/dist/exit-codes.js.map +1 -0
  13. package/dist/feature-finder.d.ts +38 -0
  14. package/dist/feature-finder.d.ts.map +1 -1
  15. package/dist/feature-finder.js +86 -8
  16. package/dist/feature-finder.js.map +1 -1
  17. package/dist/formatters/index.d.ts +44 -4
  18. package/dist/formatters/index.d.ts.map +1 -1
  19. package/dist/formatters/index.js +52 -15
  20. package/dist/formatters/index.js.map +1 -1
  21. package/dist/formatters/stylish.d.ts +3 -0
  22. package/dist/formatters/stylish.d.ts.map +1 -1
  23. package/dist/formatters/stylish.js +32 -1
  24. package/dist/formatters/stylish.js.map +1 -1
  25. package/dist/formatters/tap.d.ts +14 -0
  26. package/dist/formatters/tap.d.ts.map +1 -1
  27. package/dist/formatters/tap.js +22 -7
  28. package/dist/formatters/tap.js.map +1 -1
  29. package/dist/gherkin/parse.d.ts +42 -1
  30. package/dist/gherkin/parse.d.ts.map +1 -1
  31. package/dist/gherkin/parse.js +68 -3
  32. package/dist/gherkin/parse.js.map +1 -1
  33. package/dist/index.d.ts +27 -9
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +15 -5
  36. package/dist/index.js.map +1 -1
  37. package/dist/linter.d.ts +31 -3
  38. package/dist/linter.d.ts.map +1 -1
  39. package/dist/linter.js +82 -15
  40. package/dist/linter.js.map +1 -1
  41. package/dist/logger.d.ts +2 -0
  42. package/dist/logger.d.ts.map +1 -1
  43. package/dist/logger.js +4 -0
  44. package/dist/logger.js.map +1 -1
  45. package/dist/main.d.ts +9 -1
  46. package/dist/main.d.ts.map +1 -1
  47. package/dist/main.js +158 -31
  48. package/dist/main.js.map +1 -1
  49. package/dist/rules/no-dupe-feature-names.d.ts.map +1 -1
  50. package/dist/rules/no-dupe-feature-names.js +26 -13
  51. package/dist/rules/no-dupe-feature-names.js.map +1 -1
  52. package/dist/rules/no-dupe-file-names.d.ts.map +1 -1
  53. package/dist/rules/no-dupe-file-names.js +32 -17
  54. package/dist/rules/no-dupe-file-names.js.map +1 -1
  55. package/dist/rules/no-dupe-scenario-names.d.ts.map +1 -1
  56. package/dist/rules/no-dupe-scenario-names.js +34 -22
  57. package/dist/rules/no-dupe-scenario-names.js.map +1 -1
  58. package/dist/rules.d.ts +41 -3
  59. package/dist/rules.d.ts.map +1 -1
  60. package/dist/rules.js +112 -4
  61. package/dist/rules.js.map +1 -1
  62. package/dist/stats/collect.d.ts +31 -0
  63. package/dist/stats/collect.d.ts.map +1 -0
  64. package/dist/stats/collect.js +249 -0
  65. package/dist/stats/collect.js.map +1 -0
  66. package/dist/stats/command.d.ts +4 -0
  67. package/dist/stats/command.d.ts.map +1 -0
  68. package/dist/stats/command.js +113 -0
  69. package/dist/stats/command.js.map +1 -0
  70. package/dist/stats/format/index.d.ts +25 -0
  71. package/dist/stats/format/index.d.ts.map +1 -0
  72. package/dist/stats/format/index.js +25 -0
  73. package/dist/stats/format/index.js.map +1 -0
  74. package/dist/stats/format/json.d.ts +10 -0
  75. package/dist/stats/format/json.d.ts.map +1 -0
  76. package/dist/stats/format/json.js +5 -0
  77. package/dist/stats/format/json.js.map +1 -0
  78. package/dist/stats/format/markdown.d.ts +8 -0
  79. package/dist/stats/format/markdown.d.ts.map +1 -0
  80. package/dist/stats/format/markdown.js +123 -0
  81. package/dist/stats/format/markdown.js.map +1 -0
  82. package/dist/stats/format/shared.d.ts +34 -0
  83. package/dist/stats/format/shared.d.ts.map +1 -0
  84. package/dist/stats/format/shared.js +37 -0
  85. package/dist/stats/format/shared.js.map +1 -0
  86. package/dist/stats/format/text.d.ts +5 -0
  87. package/dist/stats/format/text.d.ts.map +1 -0
  88. package/dist/stats/format/text.js +175 -0
  89. package/dist/stats/format/text.js.map +1 -0
  90. package/dist/stats/index.d.ts +21 -0
  91. package/dist/stats/index.d.ts.map +1 -0
  92. package/dist/stats/index.js +17 -0
  93. package/dist/stats/index.js.map +1 -0
  94. package/dist/stats/normalise.d.ts +38 -0
  95. package/dist/stats/normalise.d.ts.map +1 -0
  96. package/dist/stats/normalise.js +65 -0
  97. package/dist/stats/normalise.js.map +1 -0
  98. package/dist/stats/similar.d.ts +61 -0
  99. package/dist/stats/similar.d.ts.map +1 -0
  100. package/dist/stats/similar.js +173 -0
  101. package/dist/stats/similar.js.map +1 -0
  102. package/dist/stats/types.d.ts +159 -0
  103. package/dist/stats/types.d.ts.map +1 -0
  104. package/dist/stats/types.js +10 -0
  105. package/dist/stats/types.js.map +1 -0
  106. package/dist/types.d.ts +47 -3
  107. package/dist/types.d.ts.map +1 -1
  108. package/dist/util/glob.d.ts +18 -0
  109. package/dist/util/glob.d.ts.map +1 -1
  110. package/dist/util/glob.js +126 -22
  111. package/dist/util/glob.js.map +1 -1
  112. package/dist/util/stream.d.ts +31 -0
  113. package/dist/util/stream.d.ts.map +1 -0
  114. package/dist/util/stream.js +67 -0
  115. package/dist/util/stream.js.map +1 -0
  116. package/dist/watch.d.ts +36 -0
  117. package/dist/watch.d.ts.map +1 -0
  118. package/dist/watch.js +203 -0
  119. package/dist/watch.js.map +1 -0
  120. package/package.json +1 -1
package/dist/linter.d.ts CHANGED
@@ -1,18 +1,46 @@
1
1
  import type { Configuration, FileResult, RuleRegistry } from './types.ts';
2
+ import type { Sequence } from './util/stream.ts';
2
3
  export interface LintOptions {
3
4
  /**
4
5
  * The dialect to read a file in when it carries no `# language:` header,
5
6
  * for projects written entirely in one language.
6
7
  */
7
8
  language?: string;
9
+ /**
10
+ * How many files to read ahead of the one being checked. Left out, a
11
+ * sensible default is used; there is rarely a reason to set it.
12
+ */
13
+ readAhead?: number;
8
14
  }
9
15
  /**
10
- * Lints every file and returns one result per file, in the order given.
16
+ * Lints every file, handing back each result as soon as it is final.
11
17
  *
12
18
  * Files are read concurrently but checked one after another, because rules
13
- * that look for duplicates across files need a predictable order.
19
+ * that look for duplicates across files need a predictable order. Results
20
+ * come in the order the files were given.
21
+ *
22
+ * A rule that reports across files - two files sharing a name - cannot know
23
+ * what it has found until every file has been seen, so switching one on holds
24
+ * every result back until the end. That is the honest cost of the question it
25
+ * answers, not something to work around: a result handed over early would
26
+ * have to be taken back. With no such rule on, each result arrives as its
27
+ * file is checked.
28
+ *
29
+ * Files are read ahead of being checked, a bounded number at a time, so the
30
+ * next one is on its way while this one is being checked without the whole
31
+ * suite being held in memory at once.
32
+ *
33
+ * Stopping early - `break` in a `for await` - stops the reading as well as
34
+ * the checking, rather than running on to the last file.
35
+ */
36
+ export declare function lintStream(files: Sequence<string>, configuration: Configuration, rules: RuleRegistry, options?: LintOptions): AsyncGenerator<FileResult>;
37
+ /**
38
+ * Lints every file and returns one result per file, in the order given.
39
+ *
40
+ * The whole run at once. For results as they are ready - a progress line, an
41
+ * editor, stopping at the first error - use `lintStream`.
14
42
  */
15
- export declare function lint(files: readonly string[], configuration: Configuration, rules: RuleRegistry, options?: LintOptions): Promise<FileResult[]>;
43
+ export declare function lint(files: Sequence<string>, configuration: Configuration, rules: RuleRegistry, options?: LintOptions): Promise<FileResult[]>;
16
44
  /** True when any file has a finding serious enough to fail the run. */
17
45
  export declare function hasErrors(results: readonly FileResult[]): boolean;
18
46
  /** How many findings there are of each severity. */
@@ -1 +1 @@
1
- {"version":3,"file":"linter.d.ts","sourceRoot":"","sources":["../src/linter.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAC,aAAa,EAAE,UAAU,EAAE,YAAY,EAAC,MAAM,YAAY,CAAC;AAGxE,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,IAAI,CACxB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,EAAE,CAAC,CA4BvB;AAED,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,OAAO,CAIjE;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAaA"}
1
+ {"version":3,"file":"linter.d.ts","sourceRoot":"","sources":["../src/linter.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAC,aAAa,EAAE,UAAU,EAAE,YAAY,EAAC,MAAM,YAAY,CAAC;AAExE,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAK/C,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAYD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAuB,UAAU,CAC/B,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EACvB,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE,WAAgB,GACxB,cAAc,CAAC,UAAU,CAAC,CAuD5B;AAED;;;;;GAKG;AACH,wBAAsB,IAAI,CACxB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,EACvB,aAAa,EAAE,aAAa,EAC5B,KAAK,EAAE,YAAY,EACnB,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,UAAU,EAAE,CAAC,CAMvB;AAED,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG,OAAO,CAIjE;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,UAAU,EAAE,GAAG;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB,CAaA"}
package/dist/linter.js CHANGED
@@ -2,35 +2,102 @@
2
2
  * Running the rules over a set of feature files.
3
3
  */
4
4
  import path from 'node:path';
5
- import { readAndParseFile } from './gherkin/parse.js';
6
- import { resetRules, runEnabledRules } from './rules.js';
5
+ import { readAndParseFiles } from './gherkin/parse.js';
6
+ import { beginRun, finishRun, isRuleEnabled, runEnabledRules } from './rules.js';
7
7
  import { readSuppressions } from './suppressions.js';
8
8
  import { sortBy } from './util/collections.js';
9
+ /** Stands in for a file whose directives were never read, and hides nothing. */
10
+ const EMPTY_SUPPRESSIONS = { isEmpty: true, isSuppressed: () => false };
11
+ /** True when a rule that only reports once every file has been seen is on. */
12
+ function reportsAcrossFiles(rules, configuration) {
13
+ for (const rule of rules.values()) {
14
+ if (rule.onRunEnd !== undefined && isRuleEnabled(configuration[rule.name])) {
15
+ return true;
16
+ }
17
+ }
18
+ return false;
19
+ }
9
20
  /**
10
- * Lints every file and returns one result per file, in the order given.
21
+ * Lints every file, handing back each result as soon as it is final.
11
22
  *
12
23
  * Files are read concurrently but checked one after another, because rules
13
- * that look for duplicates across files need a predictable order.
24
+ * that look for duplicates across files need a predictable order. Results
25
+ * come in the order the files were given.
26
+ *
27
+ * A rule that reports across files - two files sharing a name - cannot know
28
+ * what it has found until every file has been seen, so switching one on holds
29
+ * every result back until the end. That is the honest cost of the question it
30
+ * answers, not something to work around: a result handed over early would
31
+ * have to be taken back. With no such rule on, each result arrives as its
32
+ * file is checked.
33
+ *
34
+ * Files are read ahead of being checked, a bounded number at a time, so the
35
+ * next one is on its way while this one is being checked without the whole
36
+ * suite being held in memory at once.
37
+ *
38
+ * Stopping early - `break` in a `for await` - stops the reading as well as
39
+ * the checking, rather than running on to the last file.
14
40
  */
15
- export async function lint(files, configuration, rules, options = {}) {
16
- resetRules(rules);
17
- const parsed = await Promise.all(files.map((file) => readAndParseFile(file, options.language)));
18
- const results = [];
19
- for (const result of parsed) {
41
+ export async function* lintStream(files, configuration, rules, options = {}) {
42
+ const run = beginRun(rules, configuration);
43
+ const held = reportsAcrossFiles(rules, configuration);
44
+ const waiting = [];
45
+ /** Where to put a finding that names a file, and what may hide it there. */
46
+ const byPath = new Map();
47
+ for await (const result of readAndParseFiles(files, {
48
+ language: options.language,
49
+ readAhead: options.readAhead,
50
+ })) {
20
51
  // Parse errors are not suppressible: the file could not be read, so
21
52
  // hiding the message would leave nothing in its place.
22
53
  let errors = result.errors;
54
+ let suppressions = EMPTY_SUPPRESSIONS;
23
55
  if (errors.length === 0) {
24
- errors = await runEnabledRules(result.feature, result.file, configuration, rules);
25
- const suppressions = readSuppressions(result.file.lines);
56
+ errors = await runEnabledRules(result.feature, result.file, configuration, rules, run);
57
+ suppressions = readSuppressions(result.file.lines);
26
58
  if (!suppressions.isEmpty) {
27
59
  errors = errors.filter((error) => !suppressions.isSuppressed(error));
28
60
  }
29
61
  }
30
- results.push({
31
- filePath: path.resolve(result.file.relativePath),
32
- errors: sortBy(errors, (error) => error.line),
33
- });
62
+ const fileResult = { filePath: path.resolve(result.file.relativePath), errors };
63
+ byPath.set(result.file.relativePath, { result: fileResult, suppressions });
64
+ if (held) {
65
+ waiting.push(fileResult);
66
+ }
67
+ else {
68
+ fileResult.errors = sortBy(fileResult.errors, (error) => error.line);
69
+ yield fileResult;
70
+ }
71
+ }
72
+ // What only the whole run could show: two files sharing a name, and the
73
+ // like. A finding naming no file is about the run itself - a rule that
74
+ // failed looking back over it - and goes against the first file so that it
75
+ // is seen at all.
76
+ for (const { filePath, ...error } of await finishRun(rules, configuration, run)) {
77
+ const target = filePath === undefined ? undefined : byPath.get(filePath);
78
+ if (target === undefined) {
79
+ waiting[0]?.errors.push(error);
80
+ continue;
81
+ }
82
+ if (!target.suppressions.isSuppressed(error)) {
83
+ target.result.errors.push(error);
84
+ }
85
+ }
86
+ for (const result of waiting) {
87
+ result.errors = sortBy(result.errors, (error) => error.line);
88
+ yield result;
89
+ }
90
+ }
91
+ /**
92
+ * Lints every file and returns one result per file, in the order given.
93
+ *
94
+ * The whole run at once. For results as they are ready - a progress line, an
95
+ * editor, stopping at the first error - use `lintStream`.
96
+ */
97
+ export async function lint(files, configuration, rules, options = {}) {
98
+ const results = [];
99
+ for await (const result of lintStream(files, configuration, rules, options)) {
100
+ results.push(result);
34
101
  }
35
102
  return results;
36
103
  }
@@ -1 +1 @@
1
- {"version":3,"file":"linter.js","sourceRoot":"","sources":["../src/linter.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAC,UAAU,EAAE,eAAe,EAAC,MAAM,YAAY,CAAC;AACvD,OAAO,EAAC,gBAAgB,EAAC,MAAM,mBAAmB,CAAC;AAEnD,OAAO,EAAC,MAAM,EAAC,MAAM,uBAAuB,CAAC;AAU7C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,IAAI,CACxB,KAAwB,EACxB,aAA4B,EAC5B,KAAmB,EACnB,OAAO,GAAgB,EAAE;IAEzB,UAAU,CAAC,KAAK,CAAC,CAAC;IAElB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAC9B,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAC9D,CAAC;IACF,MAAM,OAAO,GAAiB,EAAE,CAAC;IAEjC,KAAK,MAAM,MAAM,IAAI,MAAM,EAAE,CAAC;QAC5B,oEAAoE;QACpE,uDAAuD;QACvD,IAAI,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAE3B,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,MAAM,GAAG,MAAM,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,CAAC,CAAC;YAClF,MAAM,YAAY,GAAG,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACzD,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;gBAC1B,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;YACvE,CAAC;QACH,CAAC;QAED,OAAO,CAAC,IAAI,CAAC;YACX,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC;YAChD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC;SAC9C,CAAC,CAAC;IACL,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,SAAS,CAAC,OAA8B;IACtD,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,OAAO,CAAC,KAAK,OAAO,CAAC,CACvE,CAAC;AACJ,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,eAAe,CAAC,OAA8B;IAI5D,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;gBAC9C,QAAQ,EAAE,CAAC;YACb,CAAC;iBAAM,CAAC;gBACN,MAAM,EAAE,CAAC;YACX,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,EAAC,MAAM,EAAE,QAAQ,EAAC,CAAC;AAC5B,CAAC"}
1
+ {"version":3,"file":"linter.js","sourceRoot":"","sources":["../src/linter.ts"],"names":[],"mappings":"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"}
package/dist/logger.d.ts CHANGED
@@ -6,6 +6,8 @@
6
6
  export declare function error(message: string): void;
7
7
  /** Writes a bold red message to stderr. */
8
8
  export declare function boldError(message: string): void;
9
+ /** Writes a message to stderr without colouring it. */
10
+ export declare function note(message: string): void;
9
11
  /** Text decorations used by the stylish formatter. */
10
12
  export declare const style: {
11
13
  gray: (text: string) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,sCAAsC;AACtC,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,2CAA2C;AAC3C,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,sDAAsD;AACtD,eAAO,MAAM,KAAK;IAChB,IAAI,SAAS,MAAM,KAAG,MAAM;IAC5B,SAAS,SAAS,MAAM,KAAG,MAAM;IACjC,gFAAgF;IAChF,QAAQ,aAAa,MAAM,QAAQ,MAAM,KAAG,MAAM;CAEnD,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAaH,sCAAsC;AACtC,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,2CAA2C;AAC3C,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,uDAAuD;AACvD,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,sDAAsD;AACtD,eAAO,MAAM,KAAK;IAChB,IAAI,SAAS,MAAM,KAAG,MAAM;IAC5B,SAAS,SAAS,MAAM,KAAG,MAAM;IACjC,gFAAgF;IAChF,QAAQ,aAAa,MAAM,QAAQ,MAAM,KAAG,MAAM;CAEnD,CAAC"}
package/dist/logger.js CHANGED
@@ -18,6 +18,10 @@ export function error(message) {
18
18
  export function boldError(message) {
19
19
  console.error(paint(`${ESC}31m${ESC}1m`, message));
20
20
  }
21
+ /** Writes a message to stderr without colouring it. */
22
+ export function note(message) {
23
+ console.error(message);
24
+ }
21
25
  /** Text decorations used by the stylish formatter. */
22
26
  export const style = {
23
27
  gray: (text) => paint(`${ESC}38;5;243m`, text),
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,GAAG,GAAG,SAAS,CAAC;AACtB,MAAM,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC;AAEzB,SAAS,QAAQ;IACf,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,KAAK,CAAC,KAAa,EAAE,OAAe;IAC3C,OAAO,QAAQ,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;AAC7D,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,KAAK,CAAC,OAAe;IACnC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,sDAAsD;AACtD,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,IAAI,EAAE,CAAC,IAAY,EAAU,EAAE,CAAC,KAAK,CAAC,GAAG,GAAG,WAAW,EAAE,IAAI,CAAC;IAC9D,SAAS,EAAE,CAAC,IAAY,EAAU,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,OAAO,IAAI,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACvF,gFAAgF;IAChF,QAAQ,EAAE,CAAC,QAAgB,EAAE,IAAY,EAAU,EAAE,CACnD,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,EAAE,IAAI,CAAC;CAClE,CAAC"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,GAAG,GAAG,SAAS,CAAC;AACtB,MAAM,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC;AAEzB,SAAS,QAAQ;IACf,OAAO,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,KAAK,CAAC,KAAa,EAAE,OAAe;IAC3C,OAAO,QAAQ,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,GAAG,OAAO,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;AAC7D,CAAC;AAED,sCAAsC;AACtC,MAAM,UAAU,KAAK,CAAC,OAAe;IACnC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,2CAA2C;AAC3C,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,GAAG,MAAM,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACzB,CAAC;AAED,sDAAsD;AACtD,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,IAAI,EAAE,CAAC,IAAY,EAAU,EAAE,CAAC,KAAK,CAAC,GAAG,GAAG,WAAW,EAAE,IAAI,CAAC;IAC9D,SAAS,EAAE,CAAC,IAAY,EAAU,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,OAAO,IAAI,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;IACvF,gFAAgF;IAChF,QAAQ,EAAE,CAAC,QAAgB,EAAE,IAAY,EAAU,EAAE,CACnD,KAAK,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,EAAE,IAAI,CAAC;CAClE,CAAC"}
package/dist/main.d.ts CHANGED
@@ -1,3 +1,11 @@
1
1
  #!/usr/bin/env node
2
- export declare function run(argv: readonly string[]): Promise<number>;
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
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AA6CA,wBAAsB,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAqFlE"}
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
@@ -6,19 +6,18 @@ import fs from 'node:fs';
6
6
  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
- import { DEFAULT_IGNORE_FILE_NAME, findFeatureFiles } from './feature-finder.js';
10
- import { DEFAULT_FORMAT, FORMATTERS, loadFormatter } from './formatters/index.js';
9
+ import { EXIT_LINT_ERRORS, EXIT_OK, EXIT_USAGE } from './exit-codes.js';
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';
11
13
  import { isKnownLanguage } from './gherkin/dialects.js';
12
- import { hasErrors, lint } from './linter.js';
13
- import * as logger from './logger.js';
14
+ import { hasErrors, lint, lintStream } from './linter.js';
15
+ import { SILENT, TO_STDERR } from './diagnostics.js';
14
16
  import { loadRules } from './rules.js';
17
+ import { runStats } from './stats/command.js';
15
18
  import { version } from './version.js';
16
- /** Nothing to report. */
17
- const EXIT_OK = 0;
18
- /** At least one finding serious enough to fail the run. Warnings alone do not. */
19
- const EXIT_LINT_ERRORS = 1;
20
- /** The linter could not run: bad arguments, missing or invalid config. */
21
- const EXIT_USAGE = 2;
19
+ /** Directory names a watch takes no notice of, however deep they turn up. */
20
+ const DEFAULT_IGNORED_NAMES = ['node_modules', '.git'];
22
21
  function usage() {
23
22
  return [
24
23
  'Usage: gurkencheck [options] <feature-files>',
@@ -33,14 +32,32 @@ function usage() {
33
32
  ` -i, --ignore <globs> comma separated globs to skip, overriding ${DEFAULT_IGNORE_FILE_NAME}`,
34
33
  ' -r, --rulesdir <dir> directory of custom rules; may be given more than once',
35
34
  ' -l, --language <code> dialect for files with no "# language:" header',
35
+ ' -w, --watch keep running, checking again whenever a file changes',
36
36
  ' -h, --help show this message',
37
37
  ' -v, --version show the version number',
38
38
  '',
39
+ 'Commands:',
40
+ ' stats [paths] report on the shape of your feature files,',
41
+ ' rather than on what is wrong with them',
42
+ '',
39
43
  'With no files given, the working directory is searched recursively.',
40
44
  'Documentation: https://sebs.github.io/gurkencheck/',
41
45
  ].join('\n');
42
46
  }
43
- export async function run(argv) {
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) {
55
+ // Subcommands are matched before the options are read, so that `stats` can
56
+ // accept a --format of its own without the two sets of names colliding.
57
+ // A directory really called `stats` is still lintable, as `./stats`.
58
+ if (argv[0] === 'stats') {
59
+ return runStats(argv.slice(1), diagnostics);
60
+ }
44
61
  let parsed;
45
62
  try {
46
63
  parsed = parseArgs({
@@ -51,6 +68,7 @@ export async function run(argv) {
51
68
  ignore: { type: 'string', short: 'i' },
52
69
  rulesdir: { type: 'string', short: 'r', multiple: true },
53
70
  language: { type: 'string', short: 'l' },
71
+ watch: { type: 'boolean', short: 'w' },
54
72
  help: { type: 'boolean', short: 'h' },
55
73
  version: { type: 'boolean', short: 'v' },
56
74
  },
@@ -58,8 +76,11 @@ export async function run(argv) {
58
76
  });
59
77
  }
60
78
  catch (thrown) {
61
- logger.boldError(thrown instanceof Error ? thrown.message : String(thrown));
62
- console.error(usage());
79
+ diagnostics.report({
80
+ level: 'error',
81
+ message: thrown instanceof Error ? thrown.message : String(thrown),
82
+ });
83
+ diagnostics.report({ level: 'notice', message: usage() });
63
84
  return EXIT_USAGE;
64
85
  }
65
86
  const { values, positionals } = parsed;
@@ -77,46 +98,128 @@ export async function run(argv) {
77
98
  rules = await loadRules(additionalRulesDirs);
78
99
  }
79
100
  catch (thrown) {
80
- logger.boldError(thrown instanceof Error ? thrown.message : String(thrown));
101
+ diagnostics.report({
102
+ level: 'error',
103
+ message: thrown instanceof Error ? thrown.message : String(thrown),
104
+ });
81
105
  return EXIT_USAGE;
82
106
  }
83
107
  const configuration = await readConfiguration(values.config, rules);
84
108
  if (!configuration.ok) {
85
- logger.boldError(configuration.message);
86
- for (const detail of configuration.details) {
87
- logger.error(`- ${detail}`);
88
- }
109
+ diagnostics.report({
110
+ level: 'error',
111
+ message: configuration.message,
112
+ details: configuration.details,
113
+ });
89
114
  return EXIT_USAGE;
90
115
  }
91
116
  const ignore = values.ignore?.split(',').map((pattern) => pattern.trim());
92
- const { files, invalidPatterns } = findFeatureFiles(positionals, ignore);
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);
93
121
  if (invalidPatterns.length > 0) {
94
122
  for (const pattern of invalidPatterns) {
95
- logger.boldError(`Invalid format of the feature file path/pattern: "${pattern}".`);
123
+ diagnostics.report({
124
+ level: 'error',
125
+ message: `Invalid format of the feature file path/pattern: "${pattern}".`,
126
+ });
96
127
  }
97
- logger.error('To run the linter please specify an existing feature file, directory or glob.');
128
+ diagnostics.report({
129
+ level: 'detail',
130
+ message: 'To run the linter please specify an existing feature file, directory or glob.',
131
+ });
98
132
  return EXIT_USAGE;
99
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.
100
137
  let formatter;
138
+ let streaming;
101
139
  try {
102
- formatter = await loadFormatter(values.format);
140
+ streaming = await loadStreamingFormatter(values.format);
141
+ if (streaming === undefined) {
142
+ formatter = await loadFormatter(values.format);
143
+ }
103
144
  }
104
145
  catch (thrown) {
105
- logger.boldError(thrown instanceof Error ? thrown.message : String(thrown));
146
+ diagnostics.report({
147
+ level: 'error',
148
+ message: thrown instanceof Error ? thrown.message : String(thrown),
149
+ });
106
150
  return EXIT_USAGE;
107
151
  }
108
152
  const language = values.language ?? configuration.language;
109
153
  if (language !== undefined && !isKnownLanguage(language)) {
110
- logger.boldError(`Unknown language "${language}". Use a Gherkin language code, such as "fr".`);
154
+ diagnostics.report({
155
+ level: 'error',
156
+ message: `Unknown language "${language}". Use a Gherkin language code, such as "fr".`,
157
+ });
111
158
  return EXIT_USAGE;
112
159
  }
113
- const results = await lint(files, configuration.configuration, rules, { language });
114
- // A formatter may print the output itself or hand it back as a string.
115
- const output = await formatter(results);
116
- if (typeof output === 'string' && output !== '') {
117
- console.log(output);
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;
118
221
  }
119
- return hasErrors(results) ? EXIT_LINT_ERRORS : EXIT_OK;
222
+ return failed ? EXIT_LINT_ERRORS : EXIT_OK;
120
223
  }
121
224
  /**
122
225
  * True when this file is the script node was asked to run.
@@ -137,8 +240,32 @@ function isMainModule() {
137
240
  return false;
138
241
  }
139
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
+ }
140
267
  // Only take over the process when run as a command, not when imported.
141
268
  if (isMainModule()) {
142
- process.exitCode = await run(process.argv.slice(2));
269
+ process.exitCode = await main(process.argv.slice(2));
143
270
  }
144
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":"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"}
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,EACL,wBAAwB,EACxB,YAAY,EACZ,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAC,KAAK,EAAC,MAAM,YAAY,CAAC;AACjC,OAAO,EACL,cAAc,EACd,UAAU,EACV,aAAa,EACb,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAC,eAAe,EAAC,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAC,SAAS,EAAE,IAAI,EAAE,UAAU,EAAC,MAAM,aAAa,CAAC;AACxD,OAAO,EAAC,MAAM,EAAE,SAAS,EAAC,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAC,SAAS,EAAC,MAAM,YAAY,CAAC;AAIrC,OAAO,EAAC,QAAQ,EAAC,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAC,OAAO,EAAC,MAAM,cAAc,CAAC;AAErC,6EAA6E;AAC7E,MAAM,qBAAqB,GAAG,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;AAEvD,SAAS,KAAK;IACZ,OAAO;QACL,8CAA8C;QAC9C,EAAE;QACF,2EAA2E;QAC3E,EAAE;QACF,UAAU;QACV,4CAA4C,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;QACjF,kEAAkE;QAClE,uCAAuC,cAAc,GAAG;QACxD,0DAA0D,wBAAwB,GAAG;QACrF,uEAAuE,wBAAwB,EAAE;QACjG,kFAAkF;QAClF,0EAA0E;QAC1E,gFAAgF;QAChF,6CAA6C;QAC7C,mDAAmD;QACnD,EAAE;QACF,WAAW;QACX,sEAAsE;QACtE,kEAAkE;QAClE,EAAE;QACF,qEAAqE;QACrE,oDAAoD;KACrD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,IAAuB,EACvB,WAAW,GAAgB,MAAM;IAEjC,2EAA2E;IAC3E,wEAAwE;IACxE,qEAAqE;IACrE,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC;QACxB,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;IAC9C,CAAC;IAED,IAAI,MAAM,CAAC;IACX,IAAI,CAAC;QACH,MAAM,GAAG,SAAS,CAAC;YACjB,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;YACf,OAAO,EAAE;gBACP,MAAM,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAC;gBACpC,MAAM,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAC;gBACpC,MAAM,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAC;gBACpC,QAAQ,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAC;gBACtD,QAAQ,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAC;gBACtC,KAAK,EAAE,EAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAC;gBACpC,IAAI,EAAE,EAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAC;gBACnC,OAAO,EAAE,EAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,GAAG,EAAC;aACvC;YACD,gBAAgB,EAAE,IAAI;SACvB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,WAAW,CAAC,MAAM,CAAC;YACjB,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;SACnE,CAAC,CAAC;QACH,WAAW,CAAC,MAAM,CAAC,EAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,EAAC,CAAC,CAAC;QACxD,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,EAAC,MAAM,EAAE,WAAW,EAAC,GAAG,MAAM,CAAC;IAErC,IAAI,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC;QACrB,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACvB,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,mBAAmB,GAAG,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC;IAClD,IAAI,KAAK,CAAC;IACV,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,SAAS,CAAC,mBAAmB,CAAC,CAAC;IAC/C,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,WAAW,CAAC,MAAM,CAAC;YACjB,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;SACnE,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IACpE,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC;QACtB,WAAW,CAAC,MAAM,CAAC;YACjB,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,aAAa,CAAC,OAAO;YAC9B,OAAO,EAAE,aAAa,CAAC,OAAO;SAC/B,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1E,wEAAwE;IACxE,2EAA2E;IAC3E,uDAAuD;IACvD,MAAM,EAAC,eAAe,EAAC,GAAG,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAErE,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/B,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;YACtC,WAAW,CAAC,MAAM,CAAC;gBACjB,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,qDAAqD,OAAO,IAAI;aAC1E,CAAC,CAAC;QACL,CAAC;QACD,WAAW,CAAC,MAAM,CAAC;YACjB,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,+EAA+E;SACzF,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,2EAA2E;IAC3E,wEAAwE;IACxE,sEAAsE;IACtE,IAAI,SAAS,CAAC;IACd,IAAI,SAAS,CAAC;IACd,IAAI,CAAC;QACH,SAAS,GAAG,MAAM,sBAAsB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACxD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,SAAS,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,WAAW,CAAC,MAAM,CAAC;YACjB,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;SACnE,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,aAAa,CAAC,QAAQ,CAAC;IAC3D,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzD,WAAW,CAAC,MAAM,CAAC;YACjB,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,qBAAqB,QAAQ,+CAA+C;SACtF,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,4EAA4E;IAC5E,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,GAAqB,EAAE,CACtC,qBAAqB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC;IAEnD,MAAM,SAAS,GAAG,KAAK,IAAqB,EAAE;QAC5C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,OAAO,MAAM,YAAY,CACvB,SAAS,EACT,QAAQ,EAAE,EACV,aAAa,CAAC,aAAa,EAC3B,KAAK,EACL,QAAQ,EACR,WAAW,CACZ,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,EAAE,aAAa,CAAC,aAAa,EAAE,KAAK,EAAE,EAAC,QAAQ,EAAC,CAAC,CAAC;QAEvF,uEAAuE;QACvE,IAAI,MAAM,CAAC;QACX,IAAI,CAAC;YACH,MAAM,GAAG,MAAM,SAAU,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;QAAC,OAAO,MAAM,EAAE,CAAC;YAChB,WAAW,CAAC,MAAM,CAAC;gBACjB,KAAK,EAAE,OAAO;gBACd,OAAO,EAAE,yBAAyB,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;aAC9F,CAAC,CAAC;YACH,OAAO,UAAU,CAAC;QACpB,CAAC;QACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,EAAE,EAAE,CAAC;YAChD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;QAED,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC;IACzD,CAAC,CAAC;IAEF,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1B,0EAA0E;QAC1E,wEAAwE;QACxE,iBAAiB;QACjB,OAAO,MAAM,KAAK,CAChB,YAAY,CAAC,WAAW,CAAC,EACzB,MAAM,CAAC,MAAM,IAAI,wBAAwB,EACzC,EAAC,WAAW,EAAE,MAAM,EAAE,qBAAqB,EAAC,EAC5C,SAAS,CACV,CAAC;IACJ,CAAC;IAED,OAAO,MAAM,SAAS,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,YAAY,CACzB,SAA6B,EAC7B,KAAuB,EACvB,aAA4B,EAC5B,KAAmB,EACnB,QAA4B,EAC5B,WAAwB;IAExB,MAAM,KAAK,GAAG,CAAC,IAAwB,EAAQ,EAAE;QAC/C,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YACtC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC,CAAC;IAEF,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,SAAS,EAAE,CAAC;QACxB,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACrB,IAAI,KAAK,EAAE,MAAM,MAAM,IAAI,UAAU,CAAC,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,EAAC,QAAQ,EAAC,CAAC,EAAE,CAAC;YAC/E,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,IAAI,OAAO,CAAC,KAAK,OAAO,CAAC,CAAC;YAClF,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1B,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IACrB,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,WAAW,CAAC,MAAM,CAAC;YACjB,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,yBAAyB,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE;SAC9F,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;IACpB,CAAC;IAED,OAAO,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,OAAO,CAAC;AAC7C,CAAC;AAED;;;;;;GAMG;AACH,SAAS,YAAY;IACnB,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC;QACH,OAAO,OAAO,IAAI,CAAC,GAAG,KAAK,aAAa,CAAC,EAAE,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,KAAK,UAAU,IAAI,CAAC,IAAuB;IACzC,IAAI,CAAC;QACH,OAAO,MAAM,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,MAAM,EAAE,CAAC;QAChB,SAAS,CAAC,MAAM,CAAC;YACf,KAAK,EAAE,OAAO;YACd,OAAO,EAAE,oEAAoE;SAC9E,CAAC,CAAC;QACH,SAAS,CAAC,MAAM,CAAC;YACf,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC;SACrF,CAAC,CAAC;QACH,OAAO,UAAU,CAAC;IACpB,CAAC;AACH,CAAC;AAED,uEAAuE;AACvE,IAAI,YAAY,EAAE,EAAE,CAAC;IACnB,OAAO,CAAC,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC"}
@@ -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,EAAC,MAAM,aAAa,CAAC;AAQ1C,QAAA,MAAM,IAAI,EAAE,QAoBX,CAAC;eAEa,IAAI"}
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
- reset() {
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 previous = seen.get(feature.name);
15
- if (previous === undefined) {
16
- seen.set(feature.name, [file.relativePath]);
17
- return [];
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
- const message = `Feature name is already used in: ${previous.join(', ')}`;
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;AAErC,6DAA6D;AAC7D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAoB,CAAC;AAEzC,MAAM,IAAI,GAAa;IACrB,IAAI;IACJ,KAAK;QACH,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IACD,GAAG,CAAC,OAAO,EAAE,IAAI;QACf,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC;YAC5C,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,MAAM,OAAO,GAAG,oCAAoC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1E,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACjC,OAAO,CAAC,EAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAC,CAAC,CAAC;IAC1D,CAAC;CACF,CAAC;AAEF,eAAe,IAAI,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,EAAC,MAAM,aAAa,CAAC;AAa1C,QAAA,MAAM,IAAI,EAAE,QAkBX,CAAC;eAEa,IAAI"}
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"}