gurkencheck 0.0.9 → 0.1.0
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 +30 -11
- package/dist/config-parser.d.ts +15 -1
- package/dist/config-parser.js +58 -12
- package/dist/config-verifier.d.ts +0 -1
- package/dist/config-verifier.js +71 -3
- package/dist/diagnostics.d.ts +0 -1
- package/dist/diagnostics.js +0 -1
- package/dist/exit-codes.d.ts +0 -1
- package/dist/exit-codes.js +0 -1
- package/dist/feature-finder.d.ts +0 -1
- package/dist/feature-finder.js +29 -11
- package/dist/formatters/index.d.ts +0 -1
- package/dist/formatters/index.js +2 -3
- package/dist/formatters/json.d.ts +0 -1
- package/dist/formatters/json.js +0 -1
- package/dist/formatters/junit.d.ts +1 -13
- package/dist/formatters/junit.js +36 -11
- package/dist/formatters/sarif.d.ts +0 -1
- package/dist/formatters/sarif.js +25 -7
- package/dist/formatters/stylish.d.ts +0 -1
- package/dist/formatters/stylish.js +0 -1
- package/dist/formatters/tap.d.ts +0 -1
- package/dist/formatters/tap.js +37 -5
- package/dist/gherkin/dialects.d.ts +0 -1
- package/dist/gherkin/dialects.js +0 -1
- package/dist/gherkin/keywords.d.ts +6 -3
- package/dist/gherkin/keywords.js +10 -4
- package/dist/gherkin/parse.d.ts +0 -1
- package/dist/gherkin/parse.js +19 -7
- package/dist/gherkin/traverse.d.ts +0 -1
- package/dist/gherkin/traverse.js +0 -1
- package/dist/gherkin/variables.d.ts +12 -1
- package/dist/gherkin/variables.js +24 -6
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/linter.d.ts +0 -1
- package/dist/linter.js +1 -2
- package/dist/logger.d.ts +0 -1
- package/dist/logger.js +0 -1
- package/dist/main.d.ts +0 -1
- package/dist/main.js +78 -27
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +0 -5
- package/dist/rules/allowed-tags.d.ts +0 -1
- package/dist/rules/allowed-tags.js +2 -1
- package/dist/rules/background-setup-only.d.ts +0 -1
- package/dist/rules/background-setup-only.js +0 -1
- package/dist/rules/file-name.d.ts +0 -1
- package/dist/rules/file-name.js +10 -1
- package/dist/rules/indentation.d.ts +0 -1
- package/dist/rules/indentation.js +24 -10
- package/dist/rules/index.d.ts +0 -1
- package/dist/rules/index.js +0 -1
- package/dist/rules/keywords-in-logical-order.d.ts +0 -1
- package/dist/rules/keywords-in-logical-order.js +0 -1
- package/dist/rules/max-scenarios-per-file.d.ts +0 -1
- package/dist/rules/max-scenarios-per-file.js +0 -1
- package/dist/rules/name-length.d.ts +0 -1
- package/dist/rules/name-length.js +5 -3
- package/dist/rules/new-line-at-eof.d.ts +0 -1
- package/dist/rules/new-line-at-eof.js +10 -2
- package/dist/rules/no-background-only-scenario.d.ts +0 -1
- package/dist/rules/no-background-only-scenario.js +0 -1
- package/dist/rules/no-dupe-feature-names.d.ts +0 -1
- package/dist/rules/no-dupe-feature-names.js +5 -1
- package/dist/rules/no-dupe-file-names.d.ts +0 -1
- package/dist/rules/no-dupe-file-names.js +0 -1
- package/dist/rules/no-dupe-scenario-names.d.ts +0 -1
- package/dist/rules/no-dupe-scenario-names.js +4 -1
- package/dist/rules/no-duplicate-tags.d.ts +0 -1
- package/dist/rules/no-duplicate-tags.js +0 -1
- package/dist/rules/no-empty-background.d.ts +0 -1
- package/dist/rules/no-empty-background.js +0 -1
- package/dist/rules/no-empty-file.d.ts +0 -1
- package/dist/rules/no-empty-file.js +0 -1
- package/dist/rules/no-examples-in-scenarios.d.ts +0 -1
- package/dist/rules/no-examples-in-scenarios.js +0 -1
- package/dist/rules/no-files-without-scenarios.d.ts +0 -1
- package/dist/rules/no-files-without-scenarios.js +0 -1
- package/dist/rules/no-homogenous-tags.d.ts +0 -1
- package/dist/rules/no-homogenous-tags.js +0 -1
- package/dist/rules/no-multiple-empty-lines.d.ts +0 -1
- package/dist/rules/no-multiple-empty-lines.js +2 -3
- package/dist/rules/no-partially-commented-tag-lines.d.ts +0 -1
- package/dist/rules/no-partially-commented-tag-lines.js +7 -5
- package/dist/rules/no-restricted-patterns.d.ts +0 -1
- package/dist/rules/no-restricted-patterns.js +64 -19
- package/dist/rules/no-restricted-tags.d.ts +0 -1
- package/dist/rules/no-restricted-tags.js +2 -1
- package/dist/rules/no-scenario-outlines-without-examples.d.ts +0 -1
- package/dist/rules/no-scenario-outlines-without-examples.js +0 -1
- package/dist/rules/no-scenarios-without-then.d.ts +0 -1
- package/dist/rules/no-scenarios-without-then.js +0 -1
- package/dist/rules/no-scenarios-without-when.d.ts +0 -1
- package/dist/rules/no-scenarios-without-when.js +0 -1
- package/dist/rules/no-superfluous-tags.d.ts +0 -1
- package/dist/rules/no-superfluous-tags.js +0 -1
- package/dist/rules/no-trailing-spaces.d.ts +0 -1
- package/dist/rules/no-trailing-spaces.js +13 -4
- package/dist/rules/no-undeclared-variables.d.ts +0 -1
- package/dist/rules/no-undeclared-variables.js +12 -11
- package/dist/rules/no-unnamed-features.d.ts +0 -1
- package/dist/rules/no-unnamed-features.js +0 -1
- package/dist/rules/no-unnamed-scenarios.d.ts +0 -1
- package/dist/rules/no-unnamed-scenarios.js +0 -1
- package/dist/rules/no-unused-variables.d.ts +0 -1
- package/dist/rules/no-unused-variables.js +0 -1
- package/dist/rules/one-space-between-tags.d.ts +0 -1
- package/dist/rules/one-space-between-tags.js +3 -2
- package/dist/rules/only-one-when.d.ts +0 -1
- package/dist/rules/only-one-when.js +5 -5
- package/dist/rules/required-tags.d.ts +0 -1
- package/dist/rules/required-tags.js +13 -3
- package/dist/rules/scenario-size.d.ts +0 -1
- package/dist/rules/scenario-size.js +0 -1
- package/dist/rules/use-and.d.ts +0 -1
- package/dist/rules/use-and.js +2 -2
- package/dist/rules.d.ts +0 -1
- package/dist/rules.js +49 -4
- package/dist/stats/collect.d.ts +0 -1
- package/dist/stats/collect.js +0 -1
- package/dist/stats/command.d.ts +0 -1
- package/dist/stats/command.js +24 -7
- package/dist/stats/format/index.d.ts +0 -1
- package/dist/stats/format/index.js +0 -1
- package/dist/stats/format/json.d.ts +0 -1
- package/dist/stats/format/json.js +0 -1
- package/dist/stats/format/markdown.d.ts +0 -1
- package/dist/stats/format/markdown.js +24 -6
- package/dist/stats/format/shared.d.ts +0 -1
- package/dist/stats/format/shared.js +0 -1
- package/dist/stats/format/text.d.ts +0 -1
- package/dist/stats/format/text.js +0 -1
- package/dist/stats/index.d.ts +0 -1
- package/dist/stats/index.js +0 -1
- package/dist/stats/normalise.d.ts +0 -1
- package/dist/stats/normalise.js +27 -14
- package/dist/stats/similar.d.ts +0 -1
- package/dist/stats/similar.js +0 -1
- package/dist/stats/types.d.ts +0 -1
- package/dist/stats/types.js +0 -1
- package/dist/suppressions.d.ts +6 -3
- package/dist/suppressions.js +36 -30
- package/dist/types.d.ts +7 -1
- package/dist/types.js +0 -1
- package/dist/util/case.d.ts +2 -2
- package/dist/util/case.js +17 -3
- package/dist/util/collections.d.ts +0 -1
- package/dist/util/collections.js +0 -1
- package/dist/util/glob.d.ts +9 -3
- package/dist/util/glob.js +115 -27
- package/dist/util/json.d.ts +0 -1
- package/dist/util/json.js +0 -1
- package/dist/util/lines.d.ts +6 -4
- package/dist/util/lines.js +22 -4
- package/dist/util/location.d.ts +8 -1
- package/dist/util/location.js +10 -1
- package/dist/util/patterns.d.ts +9 -0
- package/dist/util/patterns.js +21 -0
- package/dist/util/resolve.d.ts +6 -0
- package/dist/util/resolve.js +101 -0
- package/dist/util/stream.d.ts +0 -1
- package/dist/util/stream.js +3 -2
- package/dist/util/xml.d.ts +0 -1
- package/dist/util/xml.js +17 -3
- package/dist/version.d.ts +0 -1
- package/dist/version.js +0 -1
- package/dist/watch.d.ts +11 -1
- package/dist/watch.js +38 -3
- package/package.json +6 -2
- package/dist/config-parser.d.ts.map +0 -1
- package/dist/config-parser.js.map +0 -1
- package/dist/config-verifier.d.ts.map +0 -1
- package/dist/config-verifier.js.map +0 -1
- package/dist/diagnostics.d.ts.map +0 -1
- package/dist/diagnostics.js.map +0 -1
- package/dist/exit-codes.d.ts.map +0 -1
- package/dist/exit-codes.js.map +0 -1
- package/dist/feature-finder.d.ts.map +0 -1
- package/dist/feature-finder.js.map +0 -1
- package/dist/formatters/index.d.ts.map +0 -1
- package/dist/formatters/index.js.map +0 -1
- package/dist/formatters/json.d.ts.map +0 -1
- package/dist/formatters/json.js.map +0 -1
- package/dist/formatters/junit.d.ts.map +0 -1
- package/dist/formatters/junit.js.map +0 -1
- package/dist/formatters/sarif.d.ts.map +0 -1
- package/dist/formatters/sarif.js.map +0 -1
- package/dist/formatters/stylish.d.ts.map +0 -1
- package/dist/formatters/stylish.js.map +0 -1
- package/dist/formatters/tap.d.ts.map +0 -1
- package/dist/formatters/tap.js.map +0 -1
- package/dist/gherkin/dialects.d.ts.map +0 -1
- package/dist/gherkin/dialects.js.map +0 -1
- package/dist/gherkin/keywords.d.ts.map +0 -1
- package/dist/gherkin/keywords.js.map +0 -1
- package/dist/gherkin/parse.d.ts.map +0 -1
- package/dist/gherkin/parse.js.map +0 -1
- package/dist/gherkin/traverse.d.ts.map +0 -1
- package/dist/gherkin/traverse.js.map +0 -1
- package/dist/gherkin/variables.d.ts.map +0 -1
- package/dist/gherkin/variables.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/linter.d.ts.map +0 -1
- package/dist/linter.js.map +0 -1
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/main.d.ts.map +0 -1
- package/dist/main.js.map +0 -1
- package/dist/presets.d.ts.map +0 -1
- package/dist/presets.js.map +0 -1
- package/dist/rules/allowed-tags.d.ts.map +0 -1
- package/dist/rules/allowed-tags.js.map +0 -1
- package/dist/rules/background-setup-only.d.ts.map +0 -1
- package/dist/rules/background-setup-only.js.map +0 -1
- package/dist/rules/file-name.d.ts.map +0 -1
- package/dist/rules/file-name.js.map +0 -1
- package/dist/rules/indentation.d.ts.map +0 -1
- package/dist/rules/indentation.js.map +0 -1
- package/dist/rules/index.d.ts.map +0 -1
- package/dist/rules/index.js.map +0 -1
- package/dist/rules/keywords-in-logical-order.d.ts.map +0 -1
- package/dist/rules/keywords-in-logical-order.js.map +0 -1
- package/dist/rules/max-scenarios-per-file.d.ts.map +0 -1
- package/dist/rules/max-scenarios-per-file.js.map +0 -1
- package/dist/rules/name-length.d.ts.map +0 -1
- package/dist/rules/name-length.js.map +0 -1
- package/dist/rules/new-line-at-eof.d.ts.map +0 -1
- package/dist/rules/new-line-at-eof.js.map +0 -1
- package/dist/rules/no-background-only-scenario.d.ts.map +0 -1
- package/dist/rules/no-background-only-scenario.js.map +0 -1
- package/dist/rules/no-dupe-feature-names.d.ts.map +0 -1
- package/dist/rules/no-dupe-feature-names.js.map +0 -1
- package/dist/rules/no-dupe-file-names.d.ts.map +0 -1
- package/dist/rules/no-dupe-file-names.js.map +0 -1
- package/dist/rules/no-dupe-scenario-names.d.ts.map +0 -1
- package/dist/rules/no-dupe-scenario-names.js.map +0 -1
- package/dist/rules/no-duplicate-tags.d.ts.map +0 -1
- package/dist/rules/no-duplicate-tags.js.map +0 -1
- package/dist/rules/no-empty-background.d.ts.map +0 -1
- package/dist/rules/no-empty-background.js.map +0 -1
- package/dist/rules/no-empty-file.d.ts.map +0 -1
- package/dist/rules/no-empty-file.js.map +0 -1
- package/dist/rules/no-examples-in-scenarios.d.ts.map +0 -1
- package/dist/rules/no-examples-in-scenarios.js.map +0 -1
- package/dist/rules/no-files-without-scenarios.d.ts.map +0 -1
- package/dist/rules/no-files-without-scenarios.js.map +0 -1
- package/dist/rules/no-homogenous-tags.d.ts.map +0 -1
- package/dist/rules/no-homogenous-tags.js.map +0 -1
- package/dist/rules/no-multiple-empty-lines.d.ts.map +0 -1
- package/dist/rules/no-multiple-empty-lines.js.map +0 -1
- package/dist/rules/no-partially-commented-tag-lines.d.ts.map +0 -1
- package/dist/rules/no-partially-commented-tag-lines.js.map +0 -1
- package/dist/rules/no-restricted-patterns.d.ts.map +0 -1
- package/dist/rules/no-restricted-patterns.js.map +0 -1
- package/dist/rules/no-restricted-tags.d.ts.map +0 -1
- package/dist/rules/no-restricted-tags.js.map +0 -1
- package/dist/rules/no-scenario-outlines-without-examples.d.ts.map +0 -1
- package/dist/rules/no-scenario-outlines-without-examples.js.map +0 -1
- package/dist/rules/no-scenarios-without-then.d.ts.map +0 -1
- package/dist/rules/no-scenarios-without-then.js.map +0 -1
- package/dist/rules/no-scenarios-without-when.d.ts.map +0 -1
- package/dist/rules/no-scenarios-without-when.js.map +0 -1
- package/dist/rules/no-superfluous-tags.d.ts.map +0 -1
- package/dist/rules/no-superfluous-tags.js.map +0 -1
- package/dist/rules/no-trailing-spaces.d.ts.map +0 -1
- package/dist/rules/no-trailing-spaces.js.map +0 -1
- package/dist/rules/no-undeclared-variables.d.ts.map +0 -1
- package/dist/rules/no-undeclared-variables.js.map +0 -1
- package/dist/rules/no-unnamed-features.d.ts.map +0 -1
- package/dist/rules/no-unnamed-features.js.map +0 -1
- package/dist/rules/no-unnamed-scenarios.d.ts.map +0 -1
- package/dist/rules/no-unnamed-scenarios.js.map +0 -1
- package/dist/rules/no-unused-variables.d.ts.map +0 -1
- package/dist/rules/no-unused-variables.js.map +0 -1
- package/dist/rules/one-space-between-tags.d.ts.map +0 -1
- package/dist/rules/one-space-between-tags.js.map +0 -1
- package/dist/rules/only-one-when.d.ts.map +0 -1
- package/dist/rules/only-one-when.js.map +0 -1
- package/dist/rules/required-tags.d.ts.map +0 -1
- package/dist/rules/required-tags.js.map +0 -1
- package/dist/rules/scenario-size.d.ts.map +0 -1
- package/dist/rules/scenario-size.js.map +0 -1
- package/dist/rules/use-and.d.ts.map +0 -1
- package/dist/rules/use-and.js.map +0 -1
- package/dist/rules.d.ts.map +0 -1
- package/dist/rules.js.map +0 -1
- package/dist/stats/collect.d.ts.map +0 -1
- package/dist/stats/collect.js.map +0 -1
- package/dist/stats/command.d.ts.map +0 -1
- package/dist/stats/command.js.map +0 -1
- package/dist/stats/format/index.d.ts.map +0 -1
- package/dist/stats/format/index.js.map +0 -1
- package/dist/stats/format/json.d.ts.map +0 -1
- package/dist/stats/format/json.js.map +0 -1
- package/dist/stats/format/markdown.d.ts.map +0 -1
- package/dist/stats/format/markdown.js.map +0 -1
- package/dist/stats/format/shared.d.ts.map +0 -1
- package/dist/stats/format/shared.js.map +0 -1
- package/dist/stats/format/text.d.ts.map +0 -1
- package/dist/stats/format/text.js.map +0 -1
- package/dist/stats/index.d.ts.map +0 -1
- package/dist/stats/index.js.map +0 -1
- package/dist/stats/normalise.d.ts.map +0 -1
- package/dist/stats/normalise.js.map +0 -1
- package/dist/stats/similar.d.ts.map +0 -1
- package/dist/stats/similar.js.map +0 -1
- package/dist/stats/types.d.ts.map +0 -1
- package/dist/stats/types.js.map +0 -1
- package/dist/suppressions.d.ts.map +0 -1
- package/dist/suppressions.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/util/case.d.ts.map +0 -1
- package/dist/util/case.js.map +0 -1
- package/dist/util/collections.d.ts.map +0 -1
- package/dist/util/collections.js.map +0 -1
- package/dist/util/glob.d.ts.map +0 -1
- package/dist/util/glob.js.map +0 -1
- package/dist/util/json.d.ts.map +0 -1
- package/dist/util/json.js.map +0 -1
- package/dist/util/lines.d.ts.map +0 -1
- package/dist/util/lines.js.map +0 -1
- package/dist/util/location.d.ts.map +0 -1
- package/dist/util/location.js.map +0 -1
- package/dist/util/stream.d.ts.map +0 -1
- package/dist/util/stream.js.map +0 -1
- package/dist/util/xml.d.ts.map +0 -1
- package/dist/util/xml.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/dist/watch.d.ts.map +0 -1
- package/dist/watch.js.map +0 -1
package/dist/formatters/sarif.js
CHANGED
|
@@ -8,24 +8,41 @@
|
|
|
8
8
|
* See https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html
|
|
9
9
|
*/
|
|
10
10
|
import path from 'node:path';
|
|
11
|
+
import { pathToFileURL } from 'node:url';
|
|
12
|
+
import { ALWAYS_ON_RULES } from '../gherkin/parse.js';
|
|
13
|
+
import { BUILT_IN_RULES } from '../rules/index.js';
|
|
11
14
|
import { version } from '../version.js';
|
|
12
15
|
const SCHEMA = 'https://json.schemastore.org/sarif-2.1.0.json';
|
|
13
16
|
const SARIF_VERSION = '2.1.0';
|
|
14
17
|
const DOCUMENTATION = 'https://sebs.github.io/gurkencheck/';
|
|
18
|
+
/** The rules with a page on the documentation site. */
|
|
19
|
+
const DOCUMENTED_RULES = new Set([
|
|
20
|
+
...BUILT_IN_RULES.map((rule) => rule.name),
|
|
21
|
+
...ALWAYS_ON_RULES,
|
|
22
|
+
]);
|
|
15
23
|
/** SARIF's levels, which happen to line up with ours. */
|
|
16
24
|
const LEVEL = { error: 'error', warning: 'warning' };
|
|
17
25
|
function levelOf(error) {
|
|
18
26
|
return LEVEL[error.severity ?? 'error'];
|
|
19
27
|
}
|
|
20
28
|
/**
|
|
21
|
-
* Paths relative to the working directory, with forward slashes.
|
|
29
|
+
* Paths relative to the working directory, with forward slashes, as a URI.
|
|
22
30
|
*
|
|
23
31
|
* Code scanning matches a result to a file in the repository by this URI, so
|
|
24
32
|
* an absolute path from whichever machine ran the linter would match nothing.
|
|
33
|
+
* It is a URI rather than a path, so each segment is percent-encoded: a `#`
|
|
34
|
+
* in a file name would otherwise start a fragment, and `%20` read as a space.
|
|
35
|
+
*
|
|
36
|
+
* A file outside the working directory is not in the repository as code
|
|
37
|
+
* scanning sees it, so a relative `../` path would match nothing either; it
|
|
38
|
+
* gets an absolute `file:` URI, which at least names the file exactly.
|
|
25
39
|
*/
|
|
26
40
|
function uriFor(filePath, cwd) {
|
|
27
41
|
const relative = path.relative(cwd, filePath);
|
|
28
|
-
|
|
42
|
+
if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
43
|
+
return pathToFileURL(path.resolve(cwd, filePath)).href;
|
|
44
|
+
}
|
|
45
|
+
return relative.split(path.sep).map(encodeURIComponent).join('/');
|
|
29
46
|
}
|
|
30
47
|
function toResult(result, error, cwd) {
|
|
31
48
|
const physicalLocation = {
|
|
@@ -63,12 +80,14 @@ export function toSarif(results, cwd = process.cwd()) {
|
|
|
63
80
|
name: 'gurkencheck',
|
|
64
81
|
informationUri: DOCUMENTATION,
|
|
65
82
|
version: version(),
|
|
66
|
-
rules:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
})),
|
|
83
|
+
// Only the rules documented on the site get a link: one to a
|
|
84
|
+
// custom rule's page, or to unexpected-error's, would be a 404.
|
|
85
|
+
rules: ruleIds.map((id) => DOCUMENTED_RULES.has(id) ? { id, helpUri: `${DOCUMENTATION}rules/${id}.html` } : { id }),
|
|
70
86
|
},
|
|
71
87
|
},
|
|
88
|
+
// Columns count characters, as the Gherkin parser does, not the
|
|
89
|
+
// UTF-16 units some tools assume.
|
|
90
|
+
columnKind: 'unicodeCodePoints',
|
|
72
91
|
results: findings,
|
|
73
92
|
},
|
|
74
93
|
],
|
|
@@ -78,4 +97,3 @@ export function toSarif(results, cwd = process.cwd()) {
|
|
|
78
97
|
export function printResults(results) {
|
|
79
98
|
console.log(JSON.stringify(toSarif(results), null, 2));
|
|
80
99
|
}
|
|
81
|
-
//# sourceMappingURL=sarif.js.map
|
package/dist/formatters/tap.d.ts
CHANGED
|
@@ -25,4 +25,3 @@ export declare function startRun(): FormatterRun;
|
|
|
25
25
|
export declare function format(results: readonly FileResult[]): string;
|
|
26
26
|
/** Writes the TAP stream to stdout. */
|
|
27
27
|
export declare function printResults(results: readonly FileResult[]): void;
|
|
28
|
-
//# sourceMappingURL=tap.d.ts.map
|
package/dist/formatters/tap.js
CHANGED
|
@@ -1,9 +1,42 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Characters a single-quoted YAML scalar cannot hold as they are: anything
|
|
3
|
+
* YAML does not count as printable, and line breaks, which it would fold.
|
|
4
|
+
*/
|
|
5
|
+
const NEEDS_ESCAPING = /[^\t\u0020-\u007E\u0085\u00A0-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/u;
|
|
6
|
+
/**
|
|
7
|
+
* Quotes a scalar for YAML. Single quotes, in which the only character needing
|
|
8
|
+
* attention is the quote itself, unless the value holds something they cannot
|
|
9
|
+
* carry; then double quotes, which can escape anything.
|
|
4
10
|
*/
|
|
5
11
|
function quote(value) {
|
|
6
|
-
|
|
12
|
+
if (!NEEDS_ESCAPING.test(value)) {
|
|
13
|
+
return `'${value.split("'").join("''")}'`;
|
|
14
|
+
}
|
|
15
|
+
let escaped = '';
|
|
16
|
+
for (const character of value) {
|
|
17
|
+
if (character === '"' || character === '\\') {
|
|
18
|
+
escaped += `\\${character}`;
|
|
19
|
+
}
|
|
20
|
+
else if (NEEDS_ESCAPING.test(character)) {
|
|
21
|
+
const code = character.codePointAt(0);
|
|
22
|
+
escaped +=
|
|
23
|
+
code <= 0xff
|
|
24
|
+
? `\\x${code.toString(16).padStart(2, '0')}`
|
|
25
|
+
: `\\u${code.toString(16).padStart(4, '0')}`;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
escaped += character;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return `"${escaped}"`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Escapes a test point's description. An unescaped `#` starts a directive, so
|
|
35
|
+
* a failing file whose path holds `# TODO` or `# SKIP` would be read as one
|
|
36
|
+
* that is allowed to fail, and the run would pass.
|
|
37
|
+
*/
|
|
38
|
+
function description(text) {
|
|
39
|
+
return text.replace(/[\\#]/g, '\\$&');
|
|
7
40
|
}
|
|
8
41
|
function severityOf(error) {
|
|
9
42
|
return error.severity ?? 'error';
|
|
@@ -41,7 +74,7 @@ export function startRun() {
|
|
|
41
74
|
file(result) {
|
|
42
75
|
count++;
|
|
43
76
|
const status = fails(result) ? 'not ok' : 'ok';
|
|
44
|
-
return [`${status} ${count} - ${result.filePath}`, ...diagnostics(result), ''].join('\n');
|
|
77
|
+
return [`${status} ${count} - ${description(result.filePath)}`, ...diagnostics(result), ''].join('\n');
|
|
45
78
|
},
|
|
46
79
|
end: () => `1..${count}\n`,
|
|
47
80
|
};
|
|
@@ -57,4 +90,3 @@ export function format(results) {
|
|
|
57
90
|
export function printResults(results) {
|
|
58
91
|
console.log(format(results));
|
|
59
92
|
}
|
|
60
|
-
//# sourceMappingURL=tap.js.map
|
|
@@ -28,4 +28,3 @@ export declare function isKnownLanguage(language: string): boolean;
|
|
|
28
28
|
* entirely in one language set it once instead of in every file.
|
|
29
29
|
*/
|
|
30
30
|
export declare function detectLanguage(lines: readonly string[], fallback?: string): string;
|
|
31
|
-
//# sourceMappingURL=dialects.d.ts.map
|
package/dist/gherkin/dialects.js
CHANGED
|
@@ -8,10 +8,14 @@ import type { Background, Examples, Feature, Rule, Scenario, Step } from '@cucum
|
|
|
8
8
|
/** Any node that carries a Gherkin keyword. */
|
|
9
9
|
export type KeywordNode = Feature | Rule | Background | Scenario | Examples | Step;
|
|
10
10
|
/** The keyword categories used across the rules. */
|
|
11
|
-
export type NeutralKeyword = 'feature' | 'rule' | 'background' | 'scenario' | 'scenariooutline' | 'examples' | 'given' | 'when' | 'then' | 'and' | 'but' | '';
|
|
11
|
+
export type NeutralKeyword = 'feature' | 'rule' | 'background' | 'scenario' | 'scenariooutline' | 'examples' | 'given' | 'when' | 'then' | 'and' | 'but' | 'star' | '';
|
|
12
12
|
/**
|
|
13
13
|
* Maps a node's localised keyword (`Szenario`) onto the language-neutral name
|
|
14
14
|
* (`scenario`). Returns an empty string for keywords the dialect does not know.
|
|
15
|
+
*
|
|
16
|
+
* `*` is `star` of its own. Every dialect lists it under all five step
|
|
17
|
+
* keywords, so looking it up would say `given` - but like `And` it carries on
|
|
18
|
+
* from the step before it, which is how Cucumber reads it too.
|
|
15
19
|
*/
|
|
16
20
|
export declare function getNeutralKeyword(node: Pick<KeywordNode, 'keyword'>, language: string | undefined): NeutralKeyword;
|
|
17
21
|
/**
|
|
@@ -24,8 +28,7 @@ export declare function getNeutralKeyword(node: Pick<KeywordNode, 'keyword'>, la
|
|
|
24
28
|
* the empty string, the same as a keyword the dialect does not know.
|
|
25
29
|
*/
|
|
26
30
|
export declare function resolvedStepKeywords(steps: readonly Pick<Step, 'keyword'>[], language: string | undefined): NeutralKeyword[];
|
|
27
|
-
/** True when the keyword is a Given/When/Then/And/But step keyword. */
|
|
31
|
+
/** True when the keyword is a Given/When/Then/And/But/`*` step keyword. */
|
|
28
32
|
export declare function isStepKeyword(keyword: NeutralKeyword): boolean;
|
|
29
33
|
/** The display name for a node, as used in error messages. */
|
|
30
34
|
export declare function getNodeType(node: Pick<KeywordNode, 'keyword'>, language: string | undefined): string;
|
|
31
|
-
//# sourceMappingURL=keywords.d.ts.map
|
package/dist/gherkin/keywords.js
CHANGED
|
@@ -12,12 +12,19 @@ const KEYWORD_FIELDS = [
|
|
|
12
12
|
'and',
|
|
13
13
|
'but',
|
|
14
14
|
];
|
|
15
|
-
const STEP_KEYWORDS = new Set(['given', 'when', 'then', 'and', 'but']);
|
|
15
|
+
const STEP_KEYWORDS = new Set(['given', 'when', 'then', 'and', 'but', 'star']);
|
|
16
16
|
/**
|
|
17
17
|
* Maps a node's localised keyword (`Szenario`) onto the language-neutral name
|
|
18
18
|
* (`scenario`). Returns an empty string for keywords the dialect does not know.
|
|
19
|
+
*
|
|
20
|
+
* `*` is `star` of its own. Every dialect lists it under all five step
|
|
21
|
+
* keywords, so looking it up would say `given` - but like `And` it carries on
|
|
22
|
+
* from the step before it, which is how Cucumber reads it too.
|
|
19
23
|
*/
|
|
20
24
|
export function getNeutralKeyword(node, language) {
|
|
25
|
+
if (node.keyword.trim() === '*') {
|
|
26
|
+
return 'star';
|
|
27
|
+
}
|
|
21
28
|
const dialect = getDialect(language);
|
|
22
29
|
for (const field of KEYWORD_FIELDS) {
|
|
23
30
|
if (dialect[field].includes(node.keyword)) {
|
|
@@ -39,14 +46,14 @@ export function resolvedStepKeywords(steps, language) {
|
|
|
39
46
|
let carried = '';
|
|
40
47
|
return steps.map((step) => {
|
|
41
48
|
const keyword = getNeutralKeyword(step, language);
|
|
42
|
-
if (keyword === 'and' || keyword === 'but') {
|
|
49
|
+
if (keyword === 'and' || keyword === 'but' || keyword === 'star') {
|
|
43
50
|
return carried;
|
|
44
51
|
}
|
|
45
52
|
carried = keyword;
|
|
46
53
|
return keyword;
|
|
47
54
|
});
|
|
48
55
|
}
|
|
49
|
-
/** True when the keyword is a Given/When/Then/And/But step keyword. */
|
|
56
|
+
/** True when the keyword is a Given/When/Then/And/But/`*` step keyword. */
|
|
50
57
|
export function isStepKeyword(keyword) {
|
|
51
58
|
return STEP_KEYWORDS.has(keyword);
|
|
52
59
|
}
|
|
@@ -70,4 +77,3 @@ export function getNodeType(node, language) {
|
|
|
70
77
|
return isStepKeyword(keyword) ? 'Step' : '';
|
|
71
78
|
}
|
|
72
79
|
}
|
|
73
|
-
//# sourceMappingURL=keywords.js.map
|
package/dist/gherkin/parse.d.ts
CHANGED
package/dist/gherkin/parse.js
CHANGED
|
@@ -81,16 +81,26 @@ function startsWithKeyword(text, keywords) {
|
|
|
81
81
|
return keywords.some((keyword) => text.startsWith(`${keyword.trim()}:`));
|
|
82
82
|
}
|
|
83
83
|
/**
|
|
84
|
-
* True when a Background is already open above `line
|
|
84
|
+
* True when a Background is already open above `line`, in the same Feature
|
|
85
|
+
* or Rule.
|
|
85
86
|
*
|
|
86
87
|
* The parser rejects a second Background and a Background written below a
|
|
87
88
|
* Scenario in exactly the same way, but they are different mistakes with
|
|
88
|
-
* different fixes, so the lines above are checked to tell them apart.
|
|
89
|
+
* different fixes, so the lines above are checked to tell them apart. A Rule
|
|
90
|
+
* may have a Background of its own beside the Feature's, so the search stops
|
|
91
|
+
* at the Rule the line belongs to.
|
|
89
92
|
*/
|
|
90
93
|
function hasEarlierBackground(lines, line, dialect) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
for (let index = line - 2; index >= 0; index--) {
|
|
95
|
+
const text = lines[index].trim();
|
|
96
|
+
if (startsWithKeyword(text, dialect.background)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (startsWithKeyword(text, dialect.rule)) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
94
104
|
}
|
|
95
105
|
/** The step keywords of a dialect, without the `*` wildcard, for messages. */
|
|
96
106
|
function stepKeywordsFor(dialect) {
|
|
@@ -191,7 +201,10 @@ export function toLines(source) {
|
|
|
191
201
|
* language.
|
|
192
202
|
*/
|
|
193
203
|
export function parseFeature(relativePath, source, defaultLanguage = DEFAULT_LANGUAGE) {
|
|
194
|
-
|
|
204
|
+
// A byte order mark, as some Windows editors write, is not text: left in,
|
|
205
|
+
// it is one more character at the start of line 1, and every column on
|
|
206
|
+
// that line is one out.
|
|
207
|
+
const lines = toLines(source.replace(/^\uFEFF/u, ''));
|
|
195
208
|
const file = { relativePath, lines };
|
|
196
209
|
const dialect = getDialect(detectLanguage(lines, defaultLanguage));
|
|
197
210
|
const errors = [];
|
|
@@ -298,4 +311,3 @@ export const DEFAULT_READ_AHEAD = 16;
|
|
|
298
311
|
export function readAndParseFiles(files, options = {}) {
|
|
299
312
|
return mapWithWindow(files, options.readAhead ?? DEFAULT_READ_AHEAD, (file) => readAndParseFile(file, options.language));
|
|
300
313
|
}
|
|
301
|
-
//# sourceMappingURL=parse.js.map
|
package/dist/gherkin/traverse.js
CHANGED
|
@@ -14,13 +14,24 @@ export interface Position {
|
|
|
14
14
|
}
|
|
15
15
|
/** Variable name -> every place it appears. */
|
|
16
16
|
export type Sightings = Map<string, Position[]>;
|
|
17
|
+
/** The columns one Examples table declares. */
|
|
18
|
+
export interface ExamplesTable {
|
|
19
|
+
/** The line of the `Examples:` keyword. */
|
|
20
|
+
line: number;
|
|
21
|
+
columns: Set<string>;
|
|
22
|
+
}
|
|
17
23
|
/** What a Scenario Outline declares, and what it actually uses. */
|
|
18
24
|
export interface ScenarioVariables {
|
|
19
25
|
/** Column names, against the Examples header cells declaring them. */
|
|
20
26
|
declared: Sightings;
|
|
27
|
+
/**
|
|
28
|
+
* Each Examples table with a header, on its own. Cucumber runs the rows of
|
|
29
|
+
* each table separately, so a variable has to be declared in every one of
|
|
30
|
+
* them, not merely in one.
|
|
31
|
+
*/
|
|
32
|
+
tables: ExamplesTable[];
|
|
21
33
|
/** Variable names, against every node using them. */
|
|
22
34
|
used: Sightings;
|
|
23
35
|
}
|
|
24
36
|
/** Gathers the variables a Scenario Outline declares and the ones it uses. */
|
|
25
37
|
export declare function variablesOf(scenario: Scenario): ScenarioVariables;
|
|
26
|
-
//# sourceMappingURL=variables.d.ts.map
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { at } from '../util/location.js';
|
|
2
|
-
/**
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* `<name>` placeholders, as used in Scenario Outlines.
|
|
4
|
+
*
|
|
5
|
+
* Cucumber substitutes any column name written in angle brackets, so what
|
|
6
|
+
* counts as one here is only what could be a column name: not starting or
|
|
7
|
+
* ending with a space or `/`, and holding no quotes or `=`. Without that,
|
|
8
|
+
* `x < 5 and y > 3`, `<div class="x">` and `<br/>` would read as variables.
|
|
9
|
+
*/
|
|
10
|
+
const VARIABLE = /<([^<>\s"'=/](?:[^<>\n"'=]*[^<>\s"'=/])?)>/gu;
|
|
4
11
|
function note(variable, position, into) {
|
|
5
12
|
const seen = into.get(variable);
|
|
6
13
|
if (seen === undefined) {
|
|
@@ -25,12 +32,19 @@ function collect(text, position, into) {
|
|
|
25
32
|
export function variablesOf(scenario) {
|
|
26
33
|
const declared = new Map();
|
|
27
34
|
const used = new Map();
|
|
35
|
+
const tables = [];
|
|
28
36
|
for (const examples of scenario.examples) {
|
|
29
|
-
|
|
37
|
+
if (examples.tableHeader === undefined) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const columns = new Set();
|
|
41
|
+
for (const cell of examples.tableHeader.cells) {
|
|
30
42
|
if (cell.value !== '') {
|
|
31
43
|
note(cell.value, at(cell.location), declared);
|
|
44
|
+
columns.add(cell.value);
|
|
32
45
|
}
|
|
33
46
|
}
|
|
47
|
+
tables.push({ line: examples.location.line, columns });
|
|
34
48
|
}
|
|
35
49
|
collect(scenario.name, at(scenario.location), used);
|
|
36
50
|
for (const step of scenario.steps) {
|
|
@@ -42,10 +56,14 @@ export function variablesOf(scenario) {
|
|
|
42
56
|
collect(cell.value, at(cell.location), used);
|
|
43
57
|
}
|
|
44
58
|
}
|
|
59
|
+
// A doc string's content starts on the line below its opening delimiter,
|
|
60
|
+
// and each line of it is reported where it is.
|
|
45
61
|
if (step.docString !== undefined) {
|
|
46
|
-
|
|
62
|
+
const { content, location } = step.docString;
|
|
63
|
+
content.split('\n').forEach((line, index) => {
|
|
64
|
+
collect(line, { line: location.line + 1 + index, column: location.column }, used);
|
|
65
|
+
});
|
|
47
66
|
}
|
|
48
67
|
}
|
|
49
|
-
return { declared, used };
|
|
68
|
+
return { declared, tables, used };
|
|
50
69
|
}
|
|
51
|
-
//# sourceMappingURL=variables.js.map
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* import {findFeatureFiles, lint, loadRules, readConfiguration} from 'gurkencheck';
|
|
6
6
|
*
|
|
7
7
|
* const rules = await loadRules();
|
|
8
|
-
* const config = readConfiguration('.gurkencheckrc', rules);
|
|
8
|
+
* const config = await readConfiguration('.gurkencheckrc', rules);
|
|
9
9
|
* if (config.ok) {
|
|
10
10
|
* const {files} = findFeatureFiles(['features']);
|
|
11
11
|
* const results = await lint(files, config.configuration, rules);
|
|
@@ -54,4 +54,3 @@ export type { StatsFormatOptions, StatsFormatter } from './stats/format/index.ts
|
|
|
54
54
|
export { runStats, statsUsage } from './stats/command.ts';
|
|
55
55
|
export type { Distribution, Inventory, KeywordMix, LanguageEntry, ScenarioRef, ScenarioStats, SimilarGroup, Statistics, StepEntry, StepStats, TagEntry, TagStats, UnreadableFile, } from './stats/types.ts';
|
|
56
56
|
export type { Configuration, FeatureFile, FileResult, LintRule, RuleConfig, RuleError, RuleRegistry, RuleState, RunContext, RunFinding, Severity, } from './types.ts';
|
|
57
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* import {findFeatureFiles, lint, loadRules, readConfiguration} from 'gurkencheck';
|
|
6
6
|
*
|
|
7
7
|
* const rules = await loadRules();
|
|
8
|
-
* const config = readConfiguration('.gurkencheckrc', rules);
|
|
8
|
+
* const config = await readConfiguration('.gurkencheckrc', rules);
|
|
9
9
|
* if (config.ok) {
|
|
10
10
|
* const {files} = findFeatureFiles(['features']);
|
|
11
11
|
* const results = await lint(files, config.configuration, rules);
|
|
@@ -38,4 +38,3 @@ export { DEFAULT_SIMILARITY, boundedEditDistance, groupSimilar } from './stats/s
|
|
|
38
38
|
export { NUMBER_MARKER, PLACEHOLDER_MARKER, STRING_MARKER, countWords, normaliseStepText, } from './stats/normalise.js';
|
|
39
39
|
export { DEFAULT_STATS_FORMAT, DEFAULT_TOP, STATS_FORMATTERS, getStatsFormatter, toJson as statisticsToJson, toMarkdown, toText, } from './stats/format/index.js';
|
|
40
40
|
export { runStats, statsUsage } from './stats/command.js';
|
|
41
|
-
//# sourceMappingURL=index.js.map
|
package/dist/linter.d.ts
CHANGED
package/dist/linter.js
CHANGED
|
@@ -54,7 +54,7 @@ export async function* lintStream(files, configuration, rules, options = {}) {
|
|
|
54
54
|
let suppressions = EMPTY_SUPPRESSIONS;
|
|
55
55
|
if (errors.length === 0) {
|
|
56
56
|
errors = await runEnabledRules(result.feature, result.file, configuration, rules, run);
|
|
57
|
-
suppressions = readSuppressions(result.file.lines);
|
|
57
|
+
suppressions = readSuppressions(result.file.lines, result.feature);
|
|
58
58
|
if (!suppressions.isEmpty) {
|
|
59
59
|
errors = errors.filter((error) => !suppressions.isSuppressed(error));
|
|
60
60
|
}
|
|
@@ -121,4 +121,3 @@ export function countBySeverity(results) {
|
|
|
121
121
|
}
|
|
122
122
|
return { errors, warnings };
|
|
123
123
|
}
|
|
124
|
-
//# sourceMappingURL=linter.js.map
|
package/dist/logger.d.ts
CHANGED
package/dist/logger.js
CHANGED
package/dist/main.d.ts
CHANGED
package/dist/main.js
CHANGED
|
@@ -29,7 +29,8 @@ function usage() {
|
|
|
29
29
|
' or the path to a formatter of your own',
|
|
30
30
|
` (default: ${DEFAULT_FORMAT})`,
|
|
31
31
|
` -c, --config <path> configuration file (default: ${DEFAULT_CONFIG_FILE_NAME})`,
|
|
32
|
-
` -i, --ignore <globs> comma separated globs to skip, overriding ${DEFAULT_IGNORE_FILE_NAME}
|
|
32
|
+
` -i, --ignore <globs> comma separated globs to skip, overriding ${DEFAULT_IGNORE_FILE_NAME};`,
|
|
33
|
+
' may be given more than once',
|
|
33
34
|
' -r, --rulesdir <dir> directory of custom rules; may be given more than once',
|
|
34
35
|
' -l, --language <code> dialect for files with no "# language:" header',
|
|
35
36
|
' -w, --watch keep running, checking again whenever a file changes',
|
|
@@ -65,7 +66,7 @@ export async function run(argv, diagnostics = SILENT) {
|
|
|
65
66
|
options: {
|
|
66
67
|
format: { type: 'string', short: 'f' },
|
|
67
68
|
config: { type: 'string', short: 'c' },
|
|
68
|
-
ignore: { type: 'string', short: 'i' },
|
|
69
|
+
ignore: { type: 'string', short: 'i', multiple: true },
|
|
69
70
|
rulesdir: { type: 'string', short: 'r', multiple: true },
|
|
70
71
|
language: { type: 'string', short: 'l' },
|
|
71
72
|
watch: { type: 'boolean', short: 'w' },
|
|
@@ -104,16 +105,34 @@ export async function run(argv, diagnostics = SILENT) {
|
|
|
104
105
|
});
|
|
105
106
|
return EXIT_USAGE;
|
|
106
107
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
108
|
+
/**
|
|
109
|
+
* The configuration and the dialect it asks for, or `undefined` once what
|
|
110
|
+
* is wrong with them has been reported. Read again on every pass of a
|
|
111
|
+
* watch, since a change to the configuration is one of the things it
|
|
112
|
+
* checks again for.
|
|
113
|
+
*/
|
|
114
|
+
const readSettings = async () => {
|
|
115
|
+
const read = await readConfiguration(values.config, rules);
|
|
116
|
+
if (!read.ok) {
|
|
117
|
+
diagnostics.report({ level: 'error', message: read.message, details: read.details });
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
const language = values.language ?? read.language;
|
|
121
|
+
if (language !== undefined && !isKnownLanguage(language)) {
|
|
122
|
+
diagnostics.report({
|
|
123
|
+
level: 'error',
|
|
124
|
+
message: `Unknown language "${language}". Use a Gherkin language code, such as "fr".`,
|
|
125
|
+
});
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
return { configuration: read.configuration, language, files: read.files };
|
|
129
|
+
};
|
|
130
|
+
let settings = await readSettings();
|
|
131
|
+
if (settings === undefined) {
|
|
114
132
|
return EXIT_USAGE;
|
|
115
133
|
}
|
|
116
|
-
|
|
134
|
+
// Given more than once, the lists add up rather than the last one winning.
|
|
135
|
+
const ignore = values.ignore?.flatMap((list) => list.split(',').map((pattern) => pattern.trim()));
|
|
117
136
|
// Files are handed over as the walk finds them, so reading and checking
|
|
118
137
|
// start on the first one rather than after the last. Only the patterns are
|
|
119
138
|
// checked here; the walk itself happens once per pass.
|
|
@@ -149,22 +168,32 @@ export async function run(argv, diagnostics = SILENT) {
|
|
|
149
168
|
});
|
|
150
169
|
return EXIT_USAGE;
|
|
151
170
|
}
|
|
152
|
-
const language = values.language ?? configuration.language;
|
|
153
|
-
if (language !== undefined && !isKnownLanguage(language)) {
|
|
154
|
-
diagnostics.report({
|
|
155
|
-
level: 'error',
|
|
156
|
-
message: `Unknown language "${language}". Use a Gherkin language code, such as "fr".`,
|
|
157
|
-
});
|
|
158
|
-
return EXIT_USAGE;
|
|
159
|
-
}
|
|
160
171
|
// Discovery is a stream, and a stream can only be read once - so a run that
|
|
161
172
|
// may happen more than once is given a fresh one each time.
|
|
162
173
|
const discover = () => findFeatureFileStream(positionals, ignore).files;
|
|
174
|
+
let passes = 0;
|
|
163
175
|
const checkOnce = async () => {
|
|
176
|
+
if (passes++ > 0) {
|
|
177
|
+
settings = await readSettings();
|
|
178
|
+
if (settings === undefined) {
|
|
179
|
+
return EXIT_USAGE;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const { configuration, language } = settings;
|
|
164
183
|
if (streaming !== undefined) {
|
|
165
|
-
return await runStreaming(streaming, discover(), configuration
|
|
184
|
+
return await runStreaming(streaming, discover(), configuration, rules, language, diagnostics);
|
|
185
|
+
}
|
|
186
|
+
let results;
|
|
187
|
+
try {
|
|
188
|
+
results = await lint(discover(), configuration, rules, { language });
|
|
189
|
+
}
|
|
190
|
+
catch (thrown) {
|
|
191
|
+
diagnostics.report({
|
|
192
|
+
level: 'error',
|
|
193
|
+
message: `The run failed: ${thrown instanceof Error ? thrown.message : String(thrown)}`,
|
|
194
|
+
});
|
|
195
|
+
return EXIT_USAGE;
|
|
166
196
|
}
|
|
167
|
-
const results = await lint(discover(), configuration.configuration, rules, { language });
|
|
168
197
|
// A formatter may print the output itself or hand it back as a string.
|
|
169
198
|
let output;
|
|
170
199
|
try {
|
|
@@ -186,7 +215,19 @@ export async function run(argv, diagnostics = SILENT) {
|
|
|
186
215
|
// Watching never fails: a run that found something is the normal state of
|
|
187
216
|
// affairs while you are fixing it, and the exit code is only read once,
|
|
188
217
|
// when you stop.
|
|
189
|
-
|
|
218
|
+
const configPath = values.config ?? DEFAULT_CONFIG_FILE_NAME;
|
|
219
|
+
return await watch(featureRoots(positionals), configPath, {
|
|
220
|
+
diagnostics,
|
|
221
|
+
ignore: DEFAULT_IGNORED_NAMES,
|
|
222
|
+
// The configuration is watched even when it does not exist yet, so
|
|
223
|
+
// writing one is noticed; the ignore file only when --ignore is not
|
|
224
|
+
// there to overrule it.
|
|
225
|
+
files: () => [
|
|
226
|
+
configPath,
|
|
227
|
+
...(settings?.files ?? []),
|
|
228
|
+
...(ignore === undefined ? [DEFAULT_IGNORE_FILE_NAME] : []),
|
|
229
|
+
],
|
|
230
|
+
}, checkOnce);
|
|
190
231
|
}
|
|
191
232
|
return await checkOnce();
|
|
192
233
|
}
|
|
@@ -202,20 +243,31 @@ async function runStreaming(streaming, files, configuration, rules, language, di
|
|
|
202
243
|
process.stdout.write(text);
|
|
203
244
|
}
|
|
204
245
|
};
|
|
246
|
+
// Linting and writing take turns in one loop, so what failed is recorded
|
|
247
|
+
// rather than guessed at: a message blaming the formatter for a rule's
|
|
248
|
+
// mistake sends you looking in the wrong place.
|
|
249
|
+
let formatting = false;
|
|
250
|
+
const format = (step) => {
|
|
251
|
+
formatting = true;
|
|
252
|
+
const written = step();
|
|
253
|
+
formatting = false;
|
|
254
|
+
return written;
|
|
255
|
+
};
|
|
205
256
|
let failed = false;
|
|
206
257
|
try {
|
|
207
|
-
const run = streaming();
|
|
208
|
-
write(run.start?.());
|
|
258
|
+
const run = format(() => streaming());
|
|
259
|
+
write(format(() => run.start?.()));
|
|
209
260
|
for await (const result of lintStream(files, configuration, rules, { language })) {
|
|
210
261
|
failed ||= result.errors.some((error) => (error.severity ?? 'error') === 'error');
|
|
211
|
-
write(run.file(result));
|
|
262
|
+
write(format(() => run.file(result)));
|
|
212
263
|
}
|
|
213
|
-
write(run.end?.());
|
|
264
|
+
write(format(() => run.end?.()));
|
|
214
265
|
}
|
|
215
266
|
catch (thrown) {
|
|
267
|
+
const reason = thrown instanceof Error ? thrown.message : String(thrown);
|
|
216
268
|
diagnostics.report({
|
|
217
269
|
level: 'error',
|
|
218
|
-
message: `The formatter failed: ${
|
|
270
|
+
message: formatting ? `The formatter failed: ${reason}` : `The run failed: ${reason}`,
|
|
219
271
|
});
|
|
220
272
|
return EXIT_USAGE;
|
|
221
273
|
}
|
|
@@ -268,4 +320,3 @@ async function main(argv) {
|
|
|
268
320
|
if (isMainModule()) {
|
|
269
321
|
process.exitCode = await main(process.argv.slice(2));
|
|
270
322
|
}
|
|
271
|
-
//# sourceMappingURL=main.js.map
|
package/dist/presets.d.ts
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* quiet on a codebase that has never been linted.
|
|
9
9
|
*
|
|
10
10
|
* Rules about style - indentation, name length, which tags go where, whether
|
|
11
|
-
* repeated keywords become And
|
|
11
|
+
* repeated keywords become And, and whitespace such as trailing spaces or a
|
|
12
|
+
* new line at the end of the file - are deliberately left out. They are worth
|
|
12
13
|
* switching on, but that is a decision for a team to make rather than a
|
|
13
14
|
* default to inherit.
|
|
14
15
|
*/
|
|
@@ -16,4 +17,3 @@ import type { Configuration } from './types.ts';
|
|
|
16
17
|
export declare const RECOMMENDED: Configuration;
|
|
17
18
|
/** The presets that can be named in a configuration file. */
|
|
18
19
|
export declare const PRESETS: Record<string, Configuration>;
|
|
19
|
-
//# sourceMappingURL=presets.d.ts.map
|