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.
- package/README.md +134 -3
- package/dist/config-parser.d.ts.map +1 -1
- package/dist/config-parser.js +7 -1
- package/dist/config-parser.js.map +1 -1
- package/dist/diagnostics.d.ts +34 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +53 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/exit-codes.d.ts +10 -0
- package/dist/exit-codes.d.ts.map +1 -0
- package/dist/exit-codes.js +10 -0
- package/dist/exit-codes.js.map +1 -0
- package/dist/feature-finder.d.ts +38 -0
- package/dist/feature-finder.d.ts.map +1 -1
- package/dist/feature-finder.js +86 -8
- package/dist/feature-finder.js.map +1 -1
- package/dist/formatters/index.d.ts +44 -4
- package/dist/formatters/index.d.ts.map +1 -1
- package/dist/formatters/index.js +52 -15
- package/dist/formatters/index.js.map +1 -1
- package/dist/formatters/stylish.d.ts +3 -0
- package/dist/formatters/stylish.d.ts.map +1 -1
- package/dist/formatters/stylish.js +32 -1
- package/dist/formatters/stylish.js.map +1 -1
- package/dist/formatters/tap.d.ts +14 -0
- package/dist/formatters/tap.d.ts.map +1 -1
- package/dist/formatters/tap.js +22 -7
- package/dist/formatters/tap.js.map +1 -1
- package/dist/gherkin/parse.d.ts +42 -1
- package/dist/gherkin/parse.d.ts.map +1 -1
- package/dist/gherkin/parse.js +68 -3
- package/dist/gherkin/parse.js.map +1 -1
- package/dist/index.d.ts +27 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -5
- package/dist/index.js.map +1 -1
- package/dist/linter.d.ts +31 -3
- package/dist/linter.d.ts.map +1 -1
- package/dist/linter.js +82 -15
- package/dist/linter.js.map +1 -1
- package/dist/logger.d.ts +2 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +4 -0
- package/dist/logger.js.map +1 -1
- package/dist/main.d.ts +9 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +158 -31
- package/dist/main.js.map +1 -1
- package/dist/rules/no-dupe-feature-names.d.ts.map +1 -1
- package/dist/rules/no-dupe-feature-names.js +26 -13
- package/dist/rules/no-dupe-feature-names.js.map +1 -1
- package/dist/rules/no-dupe-file-names.d.ts.map +1 -1
- package/dist/rules/no-dupe-file-names.js +32 -17
- package/dist/rules/no-dupe-file-names.js.map +1 -1
- package/dist/rules/no-dupe-scenario-names.d.ts.map +1 -1
- package/dist/rules/no-dupe-scenario-names.js +34 -22
- package/dist/rules/no-dupe-scenario-names.js.map +1 -1
- package/dist/rules.d.ts +41 -3
- package/dist/rules.d.ts.map +1 -1
- package/dist/rules.js +112 -4
- package/dist/rules.js.map +1 -1
- package/dist/stats/collect.d.ts +31 -0
- package/dist/stats/collect.d.ts.map +1 -0
- package/dist/stats/collect.js +249 -0
- package/dist/stats/collect.js.map +1 -0
- package/dist/stats/command.d.ts +4 -0
- package/dist/stats/command.d.ts.map +1 -0
- package/dist/stats/command.js +113 -0
- package/dist/stats/command.js.map +1 -0
- package/dist/stats/format/index.d.ts +25 -0
- package/dist/stats/format/index.d.ts.map +1 -0
- package/dist/stats/format/index.js +25 -0
- package/dist/stats/format/index.js.map +1 -0
- package/dist/stats/format/json.d.ts +10 -0
- package/dist/stats/format/json.d.ts.map +1 -0
- package/dist/stats/format/json.js +5 -0
- package/dist/stats/format/json.js.map +1 -0
- package/dist/stats/format/markdown.d.ts +8 -0
- package/dist/stats/format/markdown.d.ts.map +1 -0
- package/dist/stats/format/markdown.js +123 -0
- package/dist/stats/format/markdown.js.map +1 -0
- package/dist/stats/format/shared.d.ts +34 -0
- package/dist/stats/format/shared.d.ts.map +1 -0
- package/dist/stats/format/shared.js +37 -0
- package/dist/stats/format/shared.js.map +1 -0
- package/dist/stats/format/text.d.ts +5 -0
- package/dist/stats/format/text.d.ts.map +1 -0
- package/dist/stats/format/text.js +175 -0
- package/dist/stats/format/text.js.map +1 -0
- package/dist/stats/index.d.ts +21 -0
- package/dist/stats/index.d.ts.map +1 -0
- package/dist/stats/index.js +17 -0
- package/dist/stats/index.js.map +1 -0
- package/dist/stats/normalise.d.ts +38 -0
- package/dist/stats/normalise.d.ts.map +1 -0
- package/dist/stats/normalise.js +65 -0
- package/dist/stats/normalise.js.map +1 -0
- package/dist/stats/similar.d.ts +61 -0
- package/dist/stats/similar.d.ts.map +1 -0
- package/dist/stats/similar.js +173 -0
- package/dist/stats/similar.js.map +1 -0
- package/dist/stats/types.d.ts +159 -0
- package/dist/stats/types.d.ts.map +1 -0
- package/dist/stats/types.js +10 -0
- package/dist/stats/types.js.map +1 -0
- package/dist/types.d.ts +47 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/util/glob.d.ts +18 -0
- package/dist/util/glob.d.ts.map +1 -1
- package/dist/util/glob.js +126 -22
- package/dist/util/glob.js.map +1 -1
- package/dist/util/stream.d.ts +31 -0
- package/dist/util/stream.d.ts.map +1 -0
- package/dist/util/stream.js +67 -0
- package/dist/util/stream.js.map +1 -0
- package/dist/watch.d.ts +36 -0
- package/dist/watch.d.ts.map +1 -0
- package/dist/watch.js +203 -0
- package/dist/watch.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The bits of formatting the reports have in common.
|
|
3
|
+
*/
|
|
4
|
+
import type { Distribution } from '../types.ts';
|
|
5
|
+
/** How many entries a list in a report shows before it is cut short. */
|
|
6
|
+
export declare const DEFAULT_TOP = 10;
|
|
7
|
+
/** What a report needs to know beyond the numbers themselves. */
|
|
8
|
+
export interface StatsFormatOptions {
|
|
9
|
+
/** How many entries each list shows. */
|
|
10
|
+
top: number;
|
|
11
|
+
}
|
|
12
|
+
/** `64%`, and `0%` rather than `NaN%` when there is nothing to divide. */
|
|
13
|
+
export declare function percent(part: number, whole: number): string;
|
|
14
|
+
/** A number with one decimal, and none at all when it is a round one. */
|
|
15
|
+
export declare function decimal(value: number): string;
|
|
16
|
+
/** `min 1 median 5 p90 11 max 27 mean 5.5` */
|
|
17
|
+
export declare function summarise(distribution: Distribution): string;
|
|
18
|
+
/** `features/login.feature:12` */
|
|
19
|
+
export declare function location(file: string, line: number): string;
|
|
20
|
+
/** `1 file` / `3 files` */
|
|
21
|
+
export declare function plural(count: number, singular: string, many?: string): string;
|
|
22
|
+
/** `… and 1 more scenario` / `… and 3 more scenarios` */
|
|
23
|
+
export declare function andMore(hidden: number, singular: string, many?: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* The first `top` entries, and how many were left out.
|
|
26
|
+
*
|
|
27
|
+
* Every list in a report is the head of a longer one, and a reader who is not
|
|
28
|
+
* told that draws conclusions from a list they think is complete.
|
|
29
|
+
*/
|
|
30
|
+
export declare function head<T>(values: readonly T[], top: number): {
|
|
31
|
+
shown: T[];
|
|
32
|
+
hidden: number;
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/stats/format/shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAE9C,wEAAwE;AACxE,eAAO,MAAM,WAAW,KAAK,CAAC;AAE9B,iEAAiE;AACjE,MAAM,WAAW,kBAAkB;IACjC,wCAAwC;IACxC,GAAG,EAAE,MAAM,CAAC;CACb;AAED,0EAA0E;AAC1E,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,yEAAyE;AACzE,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,kDAAkD;AAClD,wBAAgB,SAAS,CAAC,YAAY,EAAE,YAAY,GAAG,MAAM,CAG5D;AAED,kCAAkC;AAClC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE3D;AAED,2BAA2B;AAC3B,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,SAAiB,GAAG,MAAM,CAErF;AAED,yDAAyD;AACzD,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,SAAiB,GAAG,MAAM,CAEvF;AAED;;;;;GAKG;AACH,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG;IAAC,KAAK,EAAE,CAAC,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAC,CAEvF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** How many entries a list in a report shows before it is cut short. */
|
|
2
|
+
export const DEFAULT_TOP = 10;
|
|
3
|
+
/** `64%`, and `0%` rather than `NaN%` when there is nothing to divide. */
|
|
4
|
+
export function percent(part, whole) {
|
|
5
|
+
return `${whole === 0 ? 0 : Math.round((part / whole) * 100)}%`;
|
|
6
|
+
}
|
|
7
|
+
/** A number with one decimal, and none at all when it is a round one. */
|
|
8
|
+
export function decimal(value) {
|
|
9
|
+
return Number.isInteger(value) ? String(value) : value.toFixed(1);
|
|
10
|
+
}
|
|
11
|
+
/** `min 1 median 5 p90 11 max 27 mean 5.5` */
|
|
12
|
+
export function summarise(distribution) {
|
|
13
|
+
const { min, median, p90, max, mean } = distribution;
|
|
14
|
+
return `min ${min} median ${median} p90 ${p90} max ${max} mean ${decimal(mean)}`;
|
|
15
|
+
}
|
|
16
|
+
/** `features/login.feature:12` */
|
|
17
|
+
export function location(file, line) {
|
|
18
|
+
return `${file}:${line}`;
|
|
19
|
+
}
|
|
20
|
+
/** `1 file` / `3 files` */
|
|
21
|
+
export function plural(count, singular, many = `${singular}s`) {
|
|
22
|
+
return `${count} ${count === 1 ? singular : many}`;
|
|
23
|
+
}
|
|
24
|
+
/** `… and 1 more scenario` / `… and 3 more scenarios` */
|
|
25
|
+
export function andMore(hidden, singular, many = `${singular}s`) {
|
|
26
|
+
return `… and ${hidden} more ${hidden === 1 ? singular : many}`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The first `top` entries, and how many were left out.
|
|
30
|
+
*
|
|
31
|
+
* Every list in a report is the head of a longer one, and a reader who is not
|
|
32
|
+
* told that draws conclusions from a list they think is complete.
|
|
33
|
+
*/
|
|
34
|
+
export function head(values, top) {
|
|
35
|
+
return { shown: values.slice(0, top), hidden: Math.max(0, values.length - top) };
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=shared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shared.js","sourceRoot":"","sources":["../../../src/stats/format/shared.ts"],"names":[],"mappings":"AAKA,wEAAwE;AACxE,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAQ9B,0EAA0E;AAC1E,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,KAAa;IACjD,OAAO,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC;AAClE,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,SAAS,CAAC,YAA0B;IAClD,MAAM,EAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAC,GAAG,YAAY,CAAC;IACnD,OAAO,OAAO,GAAG,aAAa,MAAM,UAAU,GAAG,UAAU,GAAG,WAAW,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;AAC3F,CAAC;AAED,kCAAkC;AAClC,MAAM,UAAU,QAAQ,CAAC,IAAY,EAAE,IAAY;IACjD,OAAO,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;AAC3B,CAAC;AAED,2BAA2B;AAC3B,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,QAAgB,EAAE,IAAI,GAAG,GAAG,QAAQ,GAAG;IAC3E,OAAO,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACrD,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,OAAO,CAAC,MAAc,EAAE,QAAgB,EAAE,IAAI,GAAG,GAAG,QAAQ,GAAG;IAC7E,OAAO,SAAS,MAAM,SAAS,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAClE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,IAAI,CAAI,MAAoB,EAAE,GAAW;IACvD,OAAO,EAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,EAAC,CAAC;AACjF,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Statistics } from '../types.ts';
|
|
2
|
+
import type { StatsFormatOptions } from './shared.ts';
|
|
3
|
+
/** The report, as a block of text. */
|
|
4
|
+
export declare function toText(statistics: Statistics, options: StatsFormatOptions): string;
|
|
5
|
+
//# sourceMappingURL=text.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.d.ts","sourceRoot":"","sources":["../../../src/stats/format/text.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAA0B,UAAU,EAAC,MAAM,aAAa,CAAC;AAErE,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,aAAa,CAAC;AAuFpD,sCAAsC;AACtC,wBAAgB,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAqLlF"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The report as something to read in a terminal.
|
|
3
|
+
*
|
|
4
|
+
* Every list here is the head of a longer one held in the JSON report, cut to
|
|
5
|
+
* `--top` entries and marked with how many were left out.
|
|
6
|
+
*/
|
|
7
|
+
import { style } from '../../logger.js';
|
|
8
|
+
import { andMore, head, location, percent, plural, summarise } from './shared.js';
|
|
9
|
+
/** Longer than this and a step is a paragraph; the report shows the start. */
|
|
10
|
+
const TEXT_WIDTH = 90;
|
|
11
|
+
function truncate(text, width = TEXT_WIDTH) {
|
|
12
|
+
return text.length <= width ? text : `${text.slice(0, width - 1)}…`;
|
|
13
|
+
}
|
|
14
|
+
/** Lays out a block so the numbers line up under each other. */
|
|
15
|
+
function block(entries) {
|
|
16
|
+
const labelWidth = entries.reduce((widest, row) => Math.max(widest, row.label.length), 0);
|
|
17
|
+
const valueWidth = entries.reduce((widest, row) => (row.wide === true ? widest : Math.max(widest, row.value.length)), 0);
|
|
18
|
+
return entries.map((row) => {
|
|
19
|
+
const value = row.wide === true ? row.value : row.value.padStart(valueWidth);
|
|
20
|
+
const line = ` ${row.label.padEnd(labelWidth)} ${value}`;
|
|
21
|
+
return row.note === undefined ? line : `${line} ${style.gray(row.note)}`;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/** A list of `count text where` lines, with each column lined up. */
|
|
25
|
+
function listing(entries, indent = ' ') {
|
|
26
|
+
const countWidth = entries.reduce((widest, entry) => Math.max(widest, entry.count === undefined ? 0 : String(entry.count).length), 0);
|
|
27
|
+
const prefix = countWidth === 0 ? indent : `${indent}${' '.repeat(countWidth + 2)}`;
|
|
28
|
+
const textWidth = entries.reduce((widest, entry) => Math.max(widest, truncate(entry.text).length), 0);
|
|
29
|
+
return entries.map((entry) => {
|
|
30
|
+
const counted = entry.count === undefined
|
|
31
|
+
? `${prefix}${truncate(entry.text)}`
|
|
32
|
+
: `${indent}${String(entry.count).padStart(countWidth)} ${truncate(entry.text)}`;
|
|
33
|
+
if (entry.where === undefined) {
|
|
34
|
+
return counted;
|
|
35
|
+
}
|
|
36
|
+
return `${counted.padEnd(prefix.length + textWidth)} ${style.gray(entry.where)}`;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function more(hidden, singular) {
|
|
40
|
+
return hidden === 0 ? [] : [style.gray(` ${andMore(hidden, singular)}`)];
|
|
41
|
+
}
|
|
42
|
+
function stepLines(entries, counted = true) {
|
|
43
|
+
return listing(entries.map((entry) => ({
|
|
44
|
+
...(counted ? { count: entry.count } : {}),
|
|
45
|
+
text: entry.text,
|
|
46
|
+
where: location(entry.file, entry.line),
|
|
47
|
+
})));
|
|
48
|
+
}
|
|
49
|
+
function similarLines(groups) {
|
|
50
|
+
return groups.flatMap((group, index) => [...(index === 0 ? [] : ['']), ...stepLines(group.members)]);
|
|
51
|
+
}
|
|
52
|
+
/** The report, as a block of text. */
|
|
53
|
+
export function toText(statistics, options) {
|
|
54
|
+
const { files, inventory, scenarios, steps, tags, languages } = statistics;
|
|
55
|
+
if (files.total === 0) {
|
|
56
|
+
return 'No feature files found.';
|
|
57
|
+
}
|
|
58
|
+
const counted = files.unreadable.length === 0
|
|
59
|
+
? plural(files.parsed, 'file')
|
|
60
|
+
: `${files.parsed} of ${plural(files.total, 'file')}`;
|
|
61
|
+
const lines = [
|
|
62
|
+
[counted, plural(inventory.features, 'feature'), plural(scenarios.effective, 'test case')].join(', '),
|
|
63
|
+
];
|
|
64
|
+
lines.push('', 'Inventory', ...block([
|
|
65
|
+
{ label: 'Features', value: String(inventory.features) },
|
|
66
|
+
{ label: 'Rules', value: String(inventory.rules) },
|
|
67
|
+
{ label: 'Backgrounds', value: String(inventory.backgrounds) },
|
|
68
|
+
{ label: 'Scenarios', value: String(inventory.scenarios) },
|
|
69
|
+
{ label: 'Scenario Outlines', value: String(inventory.scenarioOutlines) },
|
|
70
|
+
{
|
|
71
|
+
label: 'Examples tables',
|
|
72
|
+
value: String(inventory.examplesTables),
|
|
73
|
+
note: plural(inventory.examplesRows, 'row'),
|
|
74
|
+
},
|
|
75
|
+
{ label: 'Steps', value: String(inventory.steps), note: 'as written' },
|
|
76
|
+
{ label: 'Data tables', value: String(inventory.dataTables) },
|
|
77
|
+
{ label: 'Doc strings', value: String(inventory.docStrings) },
|
|
78
|
+
]));
|
|
79
|
+
const largest = head(scenarios.largest, options.top);
|
|
80
|
+
lines.push('', 'Scenarios', ...block([
|
|
81
|
+
{
|
|
82
|
+
label: 'Test cases',
|
|
83
|
+
value: String(scenarios.effective),
|
|
84
|
+
note: 'one per Scenario, one per Examples row',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
label: 'Steps per scenario',
|
|
88
|
+
value: summarise(scenarios.stepsPerScenario),
|
|
89
|
+
note: 'Background steps excluded',
|
|
90
|
+
wide: true,
|
|
91
|
+
},
|
|
92
|
+
]));
|
|
93
|
+
if (largest.shown.length > 0) {
|
|
94
|
+
lines.push('', ' Longest', ...listing(largest.shown.map((scenario) => ({
|
|
95
|
+
count: scenario.steps,
|
|
96
|
+
text: scenario.name === '' ? '(unnamed)' : scenario.name,
|
|
97
|
+
where: location(scenario.file, scenario.line),
|
|
98
|
+
}))), ...more(largest.hidden, 'scenario'));
|
|
99
|
+
}
|
|
100
|
+
const { given, when, then, other } = steps.keywords;
|
|
101
|
+
const mix = [
|
|
102
|
+
`Given ${given} (${percent(given, steps.total)})`,
|
|
103
|
+
`When ${when} (${percent(when, steps.total)})`,
|
|
104
|
+
`Then ${then} (${percent(then, steps.total)})`,
|
|
105
|
+
...(other === 0 ? [] : [`unresolved ${other}`]),
|
|
106
|
+
].join(' ');
|
|
107
|
+
lines.push('', 'Steps', ...block([
|
|
108
|
+
{ label: 'Written', value: String(steps.total), note: 'Background steps included' },
|
|
109
|
+
{
|
|
110
|
+
label: 'Distinct',
|
|
111
|
+
value: String(steps.unique),
|
|
112
|
+
note: `${percent(steps.unique, steps.total)} of all steps - lower is more reuse`,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
label: 'Written once',
|
|
116
|
+
value: String(steps.usedOnce),
|
|
117
|
+
note: `${percent(steps.usedOnce, steps.unique)} of distinct steps`,
|
|
118
|
+
},
|
|
119
|
+
{ label: 'Words per step', value: summarise(steps.wordsPerStep), wide: true },
|
|
120
|
+
{
|
|
121
|
+
label: 'Keywords',
|
|
122
|
+
value: mix,
|
|
123
|
+
note: 'And and But resolved to what they follow',
|
|
124
|
+
wide: true,
|
|
125
|
+
},
|
|
126
|
+
]));
|
|
127
|
+
const mostUsed = head(steps.vocabulary.filter((entry) => entry.count > 1), options.top);
|
|
128
|
+
if (mostUsed.shown.length > 0) {
|
|
129
|
+
lines.push('', ' Most used', ...stepLines(mostUsed.shown), ...more(mostUsed.hidden, 'step'));
|
|
130
|
+
}
|
|
131
|
+
const once = head([...steps.vocabulary].filter((entry) => entry.count === 1).sort((a, b) => (a.text < b.text ? -1 : 1)), options.top);
|
|
132
|
+
if (once.shown.length > 0) {
|
|
133
|
+
lines.push('', ' Written once', ...stepLines(once.shown, false), ...more(once.hidden, 'step'));
|
|
134
|
+
}
|
|
135
|
+
const similar = head(steps.similar, options.top);
|
|
136
|
+
if (similar.shown.length > 0) {
|
|
137
|
+
lines.push('', ` Nearly the same (${plural(steps.similar.length, 'group')})`, ...similarLines(similar.shown), ...more(similar.hidden, 'group'));
|
|
138
|
+
}
|
|
139
|
+
const untagged = inventory.scenarios + inventory.scenarioOutlines;
|
|
140
|
+
lines.push('', 'Tags', ...block([
|
|
141
|
+
{ label: 'Written', value: String(tags.total) },
|
|
142
|
+
{ label: 'Distinct', value: String(tags.unique) },
|
|
143
|
+
{
|
|
144
|
+
label: 'Written once',
|
|
145
|
+
value: String(tags.usedOnce.length),
|
|
146
|
+
note: tags.usedOnce.length === 0 ? undefined : 'a tag written once is often a typo of one written often',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
label: 'Untagged scenarios',
|
|
150
|
+
value: String(tags.untaggedScenarios),
|
|
151
|
+
note: `${percent(tags.untaggedScenarios, untagged)} of scenarios, counting inherited tags`,
|
|
152
|
+
},
|
|
153
|
+
]));
|
|
154
|
+
const tagsUsed = head(tags.vocabulary.filter((entry) => entry.count > 1), options.top);
|
|
155
|
+
if (tagsUsed.shown.length > 0) {
|
|
156
|
+
lines.push('', ' Most used', ...listing(tagsUsed.shown.map((tag) => ({ count: tag.count, text: tag.name }))), ...more(tagsUsed.hidden, 'tag'));
|
|
157
|
+
}
|
|
158
|
+
const tagsOnce = head(tags.usedOnce, options.top);
|
|
159
|
+
if (tagsOnce.shown.length > 0) {
|
|
160
|
+
lines.push('', ' Written once', ` ${tagsOnce.shown.join(', ')}`, ...more(tagsOnce.hidden, 'tag'));
|
|
161
|
+
}
|
|
162
|
+
if (languages.length > 1) {
|
|
163
|
+
lines.push('', 'Languages', ...block(languages.map((language) => ({
|
|
164
|
+
label: language.code,
|
|
165
|
+
value: String(language.files),
|
|
166
|
+
note: language.files === 1 ? 'file' : 'files',
|
|
167
|
+
}))));
|
|
168
|
+
}
|
|
169
|
+
if (files.unreadable.length > 0) {
|
|
170
|
+
const unreadable = head(files.unreadable, options.top);
|
|
171
|
+
lines.push('', `Could not be read (${plural(files.unreadable.length, 'file')}, not counted above)`, ...unreadable.shown.map((file) => ` ${location(file.file, file.line)} ${style.gray(file.reason)}`), ...more(unreadable.hidden, 'file'));
|
|
172
|
+
}
|
|
173
|
+
return lines.join('\n');
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=text.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text.js","sourceRoot":"","sources":["../../../src/stats/format/text.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statistics: what is in a set of feature files, rather than what is wrong
|
|
3
|
+
* with them.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* import {collectStatistics, readAndParseFile, toText} from 'gurkencheck';
|
|
7
|
+
*
|
|
8
|
+
* const parsed = await Promise.all(files.map((file) => readAndParseFile(file)));
|
|
9
|
+
* console.log(toText(collectStatistics(parsed), {top: 10}));
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export { collectStatistics, distribution } from './collect.ts';
|
|
13
|
+
export type { CollectOptions } from './collect.ts';
|
|
14
|
+
export { DEFAULT_SIMILARITY, boundedEditDistance, groupSimilar } from './similar.ts';
|
|
15
|
+
export type { SimilarityOptions } from './similar.ts';
|
|
16
|
+
export { NUMBER_MARKER, PLACEHOLDER_MARKER, STRING_MARKER, countWords, normaliseStepText, } from './normalise.ts';
|
|
17
|
+
export { DEFAULT_STATS_FORMAT, DEFAULT_TOP, STATS_FORMATTERS, getStatsFormatter, toJson as statisticsToJson, toMarkdown, toText, } from './format/index.ts';
|
|
18
|
+
export type { StatsFormatOptions, StatsFormatter } from './format/index.ts';
|
|
19
|
+
export { runStats, statsUsage } from './command.ts';
|
|
20
|
+
export type * from './types.ts';
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stats/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAC,iBAAiB,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC;AAC7D,YAAY,EAAC,cAAc,EAAC,MAAM,cAAc,CAAC;AACjD,OAAO,EAAC,kBAAkB,EAAE,mBAAmB,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC;AACnF,YAAY,EAAC,iBAAiB,EAAC,MAAM,cAAc,CAAC;AACpD,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,MAAM,IAAI,gBAAgB,EAC1B,UAAU,EACV,MAAM,GACP,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAC,kBAAkB,EAAE,cAAc,EAAC,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAC,QAAQ,EAAE,UAAU,EAAC,MAAM,cAAc,CAAC;AAClD,mBAAmB,YAAY,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Statistics: what is in a set of feature files, rather than what is wrong
|
|
3
|
+
* with them.
|
|
4
|
+
*
|
|
5
|
+
* ```ts
|
|
6
|
+
* import {collectStatistics, readAndParseFile, toText} from 'gurkencheck';
|
|
7
|
+
*
|
|
8
|
+
* const parsed = await Promise.all(files.map((file) => readAndParseFile(file)));
|
|
9
|
+
* console.log(toText(collectStatistics(parsed), {top: 10}));
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export { collectStatistics, distribution } from './collect.js';
|
|
13
|
+
export { DEFAULT_SIMILARITY, boundedEditDistance, groupSimilar } from './similar.js';
|
|
14
|
+
export { NUMBER_MARKER, PLACEHOLDER_MARKER, STRING_MARKER, countWords, normaliseStepText, } from './normalise.js';
|
|
15
|
+
export { DEFAULT_STATS_FORMAT, DEFAULT_TOP, STATS_FORMATTERS, getStatsFormatter, toJson as statisticsToJson, toMarkdown, toText, } from './format/index.js';
|
|
16
|
+
export { runStats, statsUsage } from './command.js';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/stats/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAC,iBAAiB,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC;AAE7D,OAAO,EAAC,kBAAkB,EAAE,mBAAmB,EAAE,YAAY,EAAC,MAAM,cAAc,CAAC;AAEnF,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,UAAU,EACV,iBAAiB,GAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,MAAM,IAAI,gBAAgB,EAC1B,UAAU,EACV,MAAM,GACP,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAC,QAAQ,EAAE,UAAU,EAAC,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deciding when two steps are the same step.
|
|
3
|
+
*
|
|
4
|
+
* `I have 3 items in my cart` and `I have 7 items in my cart` are one step
|
|
5
|
+
* behind one step definition, and counting them as two would make every
|
|
6
|
+
* measure of reuse meaningless. So the parts a step definition captures as
|
|
7
|
+
* arguments - numbers, quoted strings, Scenario Outline placeholders - are
|
|
8
|
+
* replaced by a marker before two steps are compared.
|
|
9
|
+
*
|
|
10
|
+
* The keyword is not part of the text: the Gherkin parser hands it over
|
|
11
|
+
* separately, and `Given I am logged in` and `And I am logged in` are the
|
|
12
|
+
* same step written in two places.
|
|
13
|
+
*
|
|
14
|
+
* Each kind of argument keeps a marker of its own, so a placeholder and a
|
|
15
|
+
* value written in its place stay apart: `I have <count> items` does not
|
|
16
|
+
* become `I have 0 items`. That understates reuse a little in an outline
|
|
17
|
+
* heavy suite, and it is the trade worth making, because the normalised text
|
|
18
|
+
* is what the report shows a reader - and a `0` in a step that has no number
|
|
19
|
+
* in it would be a lie about the file. The two are put back together by the
|
|
20
|
+
* near-duplicate report in `similar.ts`, which is a couple of edits apart.
|
|
21
|
+
*/
|
|
22
|
+
/** Stands in for a Scenario Outline placeholder, whatever it was called. */
|
|
23
|
+
export declare const PLACEHOLDER_MARKER = "<>";
|
|
24
|
+
/** Stands in for a double quoted string, whatever it contained. */
|
|
25
|
+
export declare const STRING_MARKER = "\"\"";
|
|
26
|
+
/** Stands in for a number, whatever its value. */
|
|
27
|
+
export declare const NUMBER_MARKER = "0";
|
|
28
|
+
/**
|
|
29
|
+
* The form of a step text used to decide whether two steps are the same one.
|
|
30
|
+
*
|
|
31
|
+
* Single quotes are deliberately left alone: `the user's cart is 'empty'` has
|
|
32
|
+
* three of them, and a rule masking whatever sits between a pair would eat
|
|
33
|
+
* half the sentence. Cucumber's own expressions quote with `"` anyway.
|
|
34
|
+
*/
|
|
35
|
+
export declare function normaliseStepText(text: string): string;
|
|
36
|
+
/** How many words a step is made of, counted before normalisation. */
|
|
37
|
+
export declare function countWords(text: string): number;
|
|
38
|
+
//# sourceMappingURL=normalise.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalise.d.ts","sourceRoot":"","sources":["../../src/stats/normalise.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC,mEAAmE;AACnE,eAAO,MAAM,aAAa,SAAO,CAAC;AAElC,kDAAkD;AAClD,eAAO,MAAM,aAAa,MAAM,CAAC;AAiBjC;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAatD;AAED,sEAAsE;AACtE,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deciding when two steps are the same step.
|
|
3
|
+
*
|
|
4
|
+
* `I have 3 items in my cart` and `I have 7 items in my cart` are one step
|
|
5
|
+
* behind one step definition, and counting them as two would make every
|
|
6
|
+
* measure of reuse meaningless. So the parts a step definition captures as
|
|
7
|
+
* arguments - numbers, quoted strings, Scenario Outline placeholders - are
|
|
8
|
+
* replaced by a marker before two steps are compared.
|
|
9
|
+
*
|
|
10
|
+
* The keyword is not part of the text: the Gherkin parser hands it over
|
|
11
|
+
* separately, and `Given I am logged in` and `And I am logged in` are the
|
|
12
|
+
* same step written in two places.
|
|
13
|
+
*
|
|
14
|
+
* Each kind of argument keeps a marker of its own, so a placeholder and a
|
|
15
|
+
* value written in its place stay apart: `I have <count> items` does not
|
|
16
|
+
* become `I have 0 items`. That understates reuse a little in an outline
|
|
17
|
+
* heavy suite, and it is the trade worth making, because the normalised text
|
|
18
|
+
* is what the report shows a reader - and a `0` in a step that has no number
|
|
19
|
+
* in it would be a lie about the file. The two are put back together by the
|
|
20
|
+
* near-duplicate report in `similar.ts`, which is a couple of edits apart.
|
|
21
|
+
*/
|
|
22
|
+
/** Stands in for a Scenario Outline placeholder, whatever it was called. */
|
|
23
|
+
export const PLACEHOLDER_MARKER = '<>';
|
|
24
|
+
/** Stands in for a double quoted string, whatever it contained. */
|
|
25
|
+
export const STRING_MARKER = '""';
|
|
26
|
+
/** Stands in for a number, whatever its value. */
|
|
27
|
+
export const NUMBER_MARKER = '0';
|
|
28
|
+
const PLACEHOLDER = /<[^<>]*>/gu;
|
|
29
|
+
const QUOTED = /"[^"]*"/gu;
|
|
30
|
+
/**
|
|
31
|
+
* A whole number or decimal standing on its own. The word boundaries keep
|
|
32
|
+
* `1st` and `v2` intact, where the digits are part of a name rather than a
|
|
33
|
+
* value a step definition would capture.
|
|
34
|
+
*/
|
|
35
|
+
const NUMBER = /\b\d+(?:[.,]\d+)*\b/gu;
|
|
36
|
+
const WHITESPACE = /\s+/gu;
|
|
37
|
+
/** A full stop or exclamation mark at the end, which changes nothing. */
|
|
38
|
+
const TRAILING_PUNCTUATION = /[\s.!]*[.!]$/u;
|
|
39
|
+
/**
|
|
40
|
+
* The form of a step text used to decide whether two steps are the same one.
|
|
41
|
+
*
|
|
42
|
+
* Single quotes are deliberately left alone: `the user's cart is 'empty'` has
|
|
43
|
+
* three of them, and a rule masking whatever sits between a pair would eat
|
|
44
|
+
* half the sentence. Cucumber's own expressions quote with `"` anyway.
|
|
45
|
+
*/
|
|
46
|
+
export function normaliseStepText(text) {
|
|
47
|
+
return text
|
|
48
|
+
.replace(PLACEHOLDER, PLACEHOLDER_MARKER)
|
|
49
|
+
.replace(QUOTED, STRING_MARKER)
|
|
50
|
+
.replace(NUMBER, NUMBER_MARKER)
|
|
51
|
+
.toLowerCase()
|
|
52
|
+
.replace(WHITESPACE, ' ')
|
|
53
|
+
.trim()
|
|
54
|
+
// Whatever sat in front of the full stop goes with it: `I own it .` and
|
|
55
|
+
// `I own it` are one step, and a step text ending in a space is not one
|
|
56
|
+
// anybody would recognise in the report.
|
|
57
|
+
.replace(TRAILING_PUNCTUATION, '')
|
|
58
|
+
.trim();
|
|
59
|
+
}
|
|
60
|
+
/** How many words a step is made of, counted before normalisation. */
|
|
61
|
+
export function countWords(text) {
|
|
62
|
+
const trimmed = text.trim();
|
|
63
|
+
return trimmed === '' ? 0 : trimmed.split(WHITESPACE).length;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=normalise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalise.js","sourceRoot":"","sources":["../../src/stats/normalise.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,4EAA4E;AAC5E,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEvC,mEAAmE;AACnE,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC;AAElC,kDAAkD;AAClD,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AAEjC,MAAM,WAAW,GAAG,YAAY,CAAC;AACjC,MAAM,MAAM,GAAG,WAAW,CAAC;AAE3B;;;;GAIG;AACH,MAAM,MAAM,GAAG,uBAAuB,CAAC;AAEvC,MAAM,UAAU,GAAG,OAAO,CAAC;AAE3B,yEAAyE;AACzE,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,IAAI;SACR,OAAO,CAAC,WAAW,EAAE,kBAAkB,CAAC;SACxC,OAAO,CAAC,MAAM,EAAE,aAAa,CAAC;SAC9B,OAAO,CAAC,MAAM,EAAE,aAAa,CAAC;SAC9B,WAAW,EAAE;SACb,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC;SACxB,IAAI,EAAE;QACP,wEAAwE;QACxE,wEAAwE;QACxE,yCAAyC;SACxC,OAAO,CAAC,oBAAoB,EAAE,EAAE,CAAC;SACjC,IAAI,EAAE,CAAC;AACZ,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding steps that are nearly, but not quite, the same step.
|
|
3
|
+
*
|
|
4
|
+
* `I am logged in` and `I'm logged in` are one behaviour written twice, and
|
|
5
|
+
* they cost two step definitions. Normalisation cannot join them - they
|
|
6
|
+
* really are different sentences - so they are grouped by how many single
|
|
7
|
+
* character edits it takes to turn one into the other.
|
|
8
|
+
*
|
|
9
|
+
* Edit distance is quadratic in the number of distinct steps, which on a
|
|
10
|
+
* large suite is millions of comparisons, so two cheap tests run first. Both
|
|
11
|
+
* are lower bounds on the distance, never overestimates, so a pair they
|
|
12
|
+
* reject could not have been close enough anyway:
|
|
13
|
+
*
|
|
14
|
+
* - two strings are at least as far apart as their lengths differ;
|
|
15
|
+
* - every edit changes the tally of characters by at most two, so half the
|
|
16
|
+
* difference between two tallies is a floor on the distance as well.
|
|
17
|
+
*/
|
|
18
|
+
/** How alike two steps have to be before they are reported together. */
|
|
19
|
+
export interface SimilarityOptions {
|
|
20
|
+
/**
|
|
21
|
+
* `1 - distance / length`, against the longer of the two steps. At 0.85 a
|
|
22
|
+
* forty character step may differ by six edits.
|
|
23
|
+
*/
|
|
24
|
+
ratio: number;
|
|
25
|
+
/**
|
|
26
|
+
* Steps shorter than this are left out. `I wait` and `I wait 0` are two
|
|
27
|
+
* edits apart, and at that length almost everything looks like everything.
|
|
28
|
+
*/
|
|
29
|
+
minLength: number;
|
|
30
|
+
/**
|
|
31
|
+
* Edits never allowed beyond, however long the steps are.
|
|
32
|
+
*
|
|
33
|
+
* The ratio on its own lets a long step drift a long way: six edits turn
|
|
34
|
+
* `the customer opens the order in bulk` into `the customer opens the cart
|
|
35
|
+
* in bulk`, which is a different step rather than the same one spelled two
|
|
36
|
+
* ways. What this report is for is the small differences - an apostrophe, a
|
|
37
|
+
* plural, a missing article - and three edits covers those.
|
|
38
|
+
*
|
|
39
|
+
* It is also what keeps the search quick: with a low cap two steps have to
|
|
40
|
+
* be within a few characters in length before they are compared at all.
|
|
41
|
+
*/
|
|
42
|
+
maxEdits: number;
|
|
43
|
+
}
|
|
44
|
+
export declare const DEFAULT_SIMILARITY: SimilarityOptions;
|
|
45
|
+
/**
|
|
46
|
+
* The Levenshtein distance between two strings, giving up as soon as it is
|
|
47
|
+
* certain to exceed `limit` and returning `limit + 1` in that case.
|
|
48
|
+
*
|
|
49
|
+
* Every value in a row of the table is a lower bound on everything below it,
|
|
50
|
+
* so once the whole row is past the limit there is no way back.
|
|
51
|
+
*/
|
|
52
|
+
export declare function boundedEditDistance(left: string, right: string, limit: number): number;
|
|
53
|
+
/**
|
|
54
|
+
* Groups entries whose text is close enough to be the same step written two
|
|
55
|
+
* ways. Entries that are close to nothing are left out, so every group
|
|
56
|
+
* returned has at least two members, in the order they were given in.
|
|
57
|
+
*/
|
|
58
|
+
export declare function groupSimilar<T extends {
|
|
59
|
+
text: string;
|
|
60
|
+
}>(entries: readonly T[], options?: SimilarityOptions): T[][];
|
|
61
|
+
//# sourceMappingURL=similar.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similar.d.ts","sourceRoot":"","sources":["../../src/stats/similar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;;;;;;;OAWG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,eAAO,MAAM,kBAAkB,EAAE,iBAA4D,CAAC;AAwC9F;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAsCtF;AAgCD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,EACnD,OAAO,EAAE,SAAS,CAAC,EAAE,EACrB,OAAO,GAAE,iBAAsC,GAC9C,CAAC,EAAE,EAAE,CAsDP"}
|