@savvy-web/silk-effects 5.9.3 → 6.0.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/changesets/utils/issue-refs.js +37 -55
- package/commitlint/hook/rules/closes-trailer.js +11 -21
- package/index.d.ts +24 -12
- package/package.json +4 -3
- package/pr-body/references.js +11 -13
|
@@ -1,65 +1,44 @@
|
|
|
1
|
+
import { collectReferenceLists, keywordFamily } from "@effected/github-references";
|
|
2
|
+
|
|
1
3
|
//#region src/changesets/utils/issue-refs.ts
|
|
2
4
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Capture group `[1]` contains the comma-separated issue number list.
|
|
6
|
-
*
|
|
7
|
-
* @internal
|
|
8
|
-
*/
|
|
9
|
-
const CLOSES_ISSUE_PATTERN = /closes?:?\s*#?(\d+(?:, *#?\d+)*)/i;
|
|
10
|
-
/**
|
|
11
|
-
* Matches "fixes #123" or "fix: #456, #789" patterns (case-insensitive).
|
|
12
|
-
*
|
|
13
|
-
* Capture group `[1]` contains the comma-separated issue number list.
|
|
14
|
-
*
|
|
15
|
-
* @internal
|
|
16
|
-
*/
|
|
17
|
-
const FIXES_ISSUE_PATTERN = /fix(?:es)?:?\s*#?(\d+(?:, *#?\d+)*)/i;
|
|
18
|
-
/**
|
|
19
|
-
* Matches "refs #123" or "ref: #456, #789" patterns (case-insensitive).
|
|
20
|
-
*
|
|
21
|
-
* Capture group `[1]` contains the comma-separated issue number list.
|
|
22
|
-
*
|
|
23
|
-
* @internal
|
|
24
|
-
*/
|
|
25
|
-
const REFS_ISSUE_PATTERN = /refs?:?\s*#?(\d+(?:, *#?\d+)*)/i;
|
|
26
|
-
/**
|
|
27
|
-
* Pattern for splitting comma-separated issue numbers.
|
|
28
|
-
*
|
|
29
|
-
* @internal
|
|
30
|
-
*/
|
|
31
|
-
const ISSUE_NUMBER_SPLIT_PATTERN = /, */;
|
|
32
|
-
/**
|
|
33
|
-
* Extract issue numbers from a regex match against a commit message.
|
|
5
|
+
* The output category each keyword family lands in.
|
|
34
6
|
*
|
|
35
7
|
* @remarks
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* @param pattern - The regex pattern to match (must have a capture group for issue numbers)
|
|
40
|
-
* @param message - The commit message body to search
|
|
41
|
-
* @returns Array of issue number strings (without `#` prefix)
|
|
8
|
+
* The `resolve` family reads as `closes` since closing is what those keywords
|
|
9
|
+
* do; the record is total over `KeywordFamily`, so a family the kit adds
|
|
10
|
+
* without a category here is a type error, never a silent drop.
|
|
42
11
|
*
|
|
43
12
|
* @internal
|
|
44
13
|
*/
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
14
|
+
const CATEGORY_BY_FAMILY = {
|
|
15
|
+
close: "closes",
|
|
16
|
+
fix: "fixes",
|
|
17
|
+
ref: "refs",
|
|
18
|
+
resolve: "closes"
|
|
19
|
+
};
|
|
20
|
+
/** @internal */
|
|
21
|
+
function categoryOf(list) {
|
|
22
|
+
return CATEGORY_BY_FAMILY[keywordFamily(list.keyword)];
|
|
50
23
|
}
|
|
51
24
|
/**
|
|
52
25
|
* Parse a commit message for GitHub issue references.
|
|
53
26
|
*
|
|
54
|
-
* Recognizes
|
|
55
|
-
*
|
|
56
|
-
* - `Fixes: #456, #789`
|
|
57
|
-
* - `
|
|
27
|
+
* Recognizes GitHub's closing keywords plus `Ref`/`Refs`/`References` in
|
|
28
|
+
* both list dialects:
|
|
29
|
+
* - whole-line trailers — `Closes #123`, `Fixes: #456, #789`, `Refs #101`;
|
|
30
|
+
* - inline in prose — `This closes #12 in passing`, or several lists on one
|
|
31
|
+
* line: `Closes #123, Fixes #456`.
|
|
58
32
|
*
|
|
59
33
|
* @remarks
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
34
|
+
* References accumulate in message order: two `Closes` lists contribute to
|
|
35
|
+
* the same category. Keywords are case-insensitive and the `#` is mandatory.
|
|
36
|
+
* The colon spelling is line-dialect-only; `collectReferenceLists` owns the
|
|
37
|
+
* per-line composition (whole-line trailer parse first, inline harvest
|
|
38
|
+
* otherwise), guaranteeing a trailer line is never double-counted. Malformed
|
|
39
|
+
* candidates are simply skipped. The kit preserves duplicates by contract;
|
|
40
|
+
* each category dedupes them here (first occurrence wins), mirroring the
|
|
41
|
+
* PR-body path, so a repeated reference renders one changelog link.
|
|
63
42
|
*
|
|
64
43
|
* @param commitMessage - The commit message body to parse
|
|
65
44
|
* @returns Categorized issue references with numbers as strings (no `#` prefix)
|
|
@@ -68,20 +47,23 @@ function extractIssueNumbers(pattern, message) {
|
|
|
68
47
|
* ```typescript
|
|
69
48
|
* import { parseIssueReferences } from "../utils/issue-refs.js";
|
|
70
49
|
*
|
|
71
|
-
* const refs = parseIssueReferences("feat: add API\n\nCloses #42
|
|
50
|
+
* const refs = parseIssueReferences("feat: add API\n\nCloses #42, Fixes #17");
|
|
72
51
|
* // refs.closes === ["42"]
|
|
73
|
-
* // refs.fixes === ["17"
|
|
52
|
+
* // refs.fixes === ["17"]
|
|
74
53
|
* // refs.refs === []
|
|
75
54
|
* ```
|
|
76
55
|
*
|
|
77
56
|
* @internal
|
|
78
57
|
*/
|
|
79
58
|
function parseIssueReferences(commitMessage) {
|
|
80
|
-
|
|
81
|
-
closes:
|
|
82
|
-
fixes:
|
|
83
|
-
refs:
|
|
59
|
+
const references = {
|
|
60
|
+
closes: [],
|
|
61
|
+
fixes: [],
|
|
62
|
+
refs: []
|
|
84
63
|
};
|
|
64
|
+
for (const list of collectReferenceLists(commitMessage)) references[categoryOf(list)].push(...list.issueNumbers.map(String));
|
|
65
|
+
for (const category of Object.keys(references)) references[category] = [...new Set(references[category])];
|
|
66
|
+
return references;
|
|
85
67
|
}
|
|
86
68
|
|
|
87
69
|
//#endregion
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Effect } from "effect";
|
|
2
|
+
import { parseClosingLists } from "@effected/github-references";
|
|
2
3
|
|
|
3
4
|
//#region src/commitlint/hook/rules/closes-trailer.ts
|
|
4
5
|
/**
|
|
@@ -8,33 +9,22 @@ import { Effect } from "effect";
|
|
|
8
9
|
* @internal
|
|
9
10
|
*/
|
|
10
11
|
/**
|
|
11
|
-
*
|
|
12
|
+
* Whether some line of the message is a closing trailer naming `ticketId`.
|
|
12
13
|
*
|
|
13
14
|
* @remarks
|
|
14
|
-
* The
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* `
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* @internal
|
|
23
|
-
*/
|
|
24
|
-
const CLOSING_LIST_PATTERN = /\b(?:closes|fixes|resolves):?\s+(#\d+(?:\s*(?:,|and)\s*#\d+)*)/gi;
|
|
25
|
-
/**
|
|
26
|
-
* Reference ids within a captured closing list.
|
|
15
|
+
* The message is read with `@effected/github-references`' `parseClosingLists`:
|
|
16
|
+
* per line, the whole line, after trimming, must be a closing keyword (any of
|
|
17
|
+
* GitHub's nine tenses, optional colon) followed by its full `#N` list —
|
|
18
|
+
* comma, `and`, or Oxford `, and` separated — so every id in the house
|
|
19
|
+
* one-trailer format (`Closes #247, #248, #251`) counts, not just the first.
|
|
20
|
+
* A `#N` mentioned mid-prose never qualifies: a trailer is a line of its own,
|
|
21
|
+
* which is why this rule deliberately stays on the whole-line dialect rather
|
|
22
|
+
* than the inline harvest.
|
|
27
23
|
*
|
|
28
24
|
* @internal
|
|
29
25
|
*/
|
|
30
|
-
const REFERENCE_PATTERN = /#(\d+)/g;
|
|
31
26
|
function hasClosingTrailer(message, ticketId) {
|
|
32
|
-
|
|
33
|
-
const list = keywordMatch[1];
|
|
34
|
-
if (list === void 0) continue;
|
|
35
|
-
for (const reference of list.matchAll(REFERENCE_PATTERN)) if (Number(reference[1]) === ticketId) return true;
|
|
36
|
-
}
|
|
37
|
-
return false;
|
|
27
|
+
return parseClosingLists(message).some((list) => list.issueNumbers.includes(ticketId));
|
|
38
28
|
}
|
|
39
29
|
const closesTrailerRule = {
|
|
40
30
|
id: "closes-trailer",
|
package/index.d.ts
CHANGED
|
@@ -6497,6 +6497,21 @@ interface ClosesTrailerCtx {
|
|
|
6497
6497
|
branchInfo: BranchInfo;
|
|
6498
6498
|
openIssues: ReadonlyArray<OpenIssue>;
|
|
6499
6499
|
}
|
|
6500
|
+
/**
|
|
6501
|
+
* Whether some line of the message is a closing trailer naming `ticketId`.
|
|
6502
|
+
*
|
|
6503
|
+
* @remarks
|
|
6504
|
+
* The message is read with `@effected/github-references`' `parseClosingLists`:
|
|
6505
|
+
* per line, the whole line, after trimming, must be a closing keyword (any of
|
|
6506
|
+
* GitHub's nine tenses, optional colon) followed by its full `#N` list —
|
|
6507
|
+
* comma, `and`, or Oxford `, and` separated — so every id in the house
|
|
6508
|
+
* one-trailer format (`Closes #247, #248, #251`) counts, not just the first.
|
|
6509
|
+
* A `#N` mentioned mid-prose never qualifies: a trailer is a line of its own,
|
|
6510
|
+
* which is why this rule deliberately stays on the whole-line dialect rather
|
|
6511
|
+
* than the inline harvest.
|
|
6512
|
+
*
|
|
6513
|
+
* @internal
|
|
6514
|
+
*/
|
|
6500
6515
|
declare function hasClosingTrailer(message: string, ticketId: number): boolean;
|
|
6501
6516
|
declare const closesTrailerRule: Rule<ClosesTrailerInput, ClosesTrailerCtx>;
|
|
6502
6517
|
//#endregion
|
|
@@ -8608,18 +8623,6 @@ declare const ClosingReferences_base: Schema.Class<ClosingReferences, Schema.Str
|
|
|
8608
8623
|
* @public
|
|
8609
8624
|
*/
|
|
8610
8625
|
declare class ClosingReferences extends ClosingReferences_base {
|
|
8611
|
-
/**
|
|
8612
|
-
* A closing keyword and its issue reference, anchored per line.
|
|
8613
|
-
*
|
|
8614
|
-
* @remarks
|
|
8615
|
-
* Anchored so a number mentioned in passing is not mistaken for a closing
|
|
8616
|
-
* reference — matching what GitHub itself links on. Every keyword GitHub
|
|
8617
|
-
* accepts is matched, not just the present-tense plural this contract
|
|
8618
|
-
* emits: `close`/`closed`, `fix`/`fixed`, `resolve`/`resolved` and an
|
|
8619
|
-
* optional colon are all valid, and a reference the parser fails to
|
|
8620
|
-
* recognise is one the next regeneration silently deletes.
|
|
8621
|
-
*/
|
|
8622
|
-
static readonly BARE_LINE_PATTERN: RegExp;
|
|
8623
8626
|
/**
|
|
8624
8627
|
* The open issues' ids, in input order, duplicates preserved.
|
|
8625
8628
|
*
|
|
@@ -8634,6 +8637,15 @@ declare class ClosingReferences extends ClosingReferences_base {
|
|
|
8634
8637
|
/**
|
|
8635
8638
|
* Issue ids carried by a region's bare closing lines.
|
|
8636
8639
|
*
|
|
8640
|
+
* @remarks
|
|
8641
|
+
* The region is read with `@effected/github-references`' `parseBareLines`
|
|
8642
|
+
* — per line, the whole line, after trimming, must be
|
|
8643
|
+
* `<keyword>[:] #<number>`, so a number mentioned in passing is never
|
|
8644
|
+
* mistaken for a closing reference. Every keyword GitHub accepts counts,
|
|
8645
|
+
* not just the present-tense plural this contract emits, because a
|
|
8646
|
+
* reference the parser fails to recognise is one the next regeneration
|
|
8647
|
+
* silently deletes.
|
|
8648
|
+
*
|
|
8637
8649
|
* @public
|
|
8638
8650
|
*/
|
|
8639
8651
|
static parseBare(region: string): ReadonlyArray<number>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@savvy-web/silk-effects",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Shared Effect library for Silk Suite conventions",
|
|
6
6
|
"homepage": "https://github.com/savvy-web/systems/tree/main/packages/silk-effects",
|
|
@@ -35,12 +35,13 @@
|
|
|
35
35
|
"@changesets/get-release-plan": "^5.0.0",
|
|
36
36
|
"@effected/commands": "^0.5.0",
|
|
37
37
|
"@effected/git": "^0.9.0",
|
|
38
|
+
"@effected/github-references": "^0.1.0",
|
|
38
39
|
"@effected/glob": "^0.4.0",
|
|
39
40
|
"@effected/jsonc": "^0.7.0",
|
|
40
|
-
"@effected/package-json": "^0.10.
|
|
41
|
+
"@effected/package-json": "^0.10.1",
|
|
41
42
|
"@effected/templates": "^0.4.0",
|
|
42
43
|
"@effected/walker": "^0.5.0",
|
|
43
|
-
"@effected/workspaces": "^0.14.
|
|
44
|
+
"@effected/workspaces": "^0.14.1",
|
|
44
45
|
"@effected/yaml": "^0.10.0",
|
|
45
46
|
"@manypkg/get-packages": "^3.1.0",
|
|
46
47
|
"mdast-util-heading-range": "^4.0.0",
|
package/pr-body/references.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Markers } from "./markers.js";
|
|
2
2
|
import { LinkedIssueRef } from "./linked-issue.js";
|
|
3
3
|
import { Schema } from "effect";
|
|
4
|
+
import { parseBareLines } from "@effected/github-references";
|
|
4
5
|
|
|
5
6
|
//#region src/pr-body/references.ts
|
|
6
7
|
/**
|
|
@@ -32,18 +33,6 @@ import { Schema } from "effect";
|
|
|
32
33
|
* @public
|
|
33
34
|
*/
|
|
34
35
|
var ClosingReferences = class ClosingReferences extends Schema.Class("ClosingReferences")({ ids: Schema.Array(Schema.Number.check(Schema.isInt())) }) {
|
|
35
|
-
/**
|
|
36
|
-
* A closing keyword and its issue reference, anchored per line.
|
|
37
|
-
*
|
|
38
|
-
* @remarks
|
|
39
|
-
* Anchored so a number mentioned in passing is not mistaken for a closing
|
|
40
|
-
* reference — matching what GitHub itself links on. Every keyword GitHub
|
|
41
|
-
* accepts is matched, not just the present-tense plural this contract
|
|
42
|
-
* emits: `close`/`closed`, `fix`/`fixed`, `resolve`/`resolved` and an
|
|
43
|
-
* optional colon are all valid, and a reference the parser fails to
|
|
44
|
-
* recognise is one the next regeneration silently deletes.
|
|
45
|
-
*/
|
|
46
|
-
static BARE_LINE_PATTERN = /^(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?):?\s+#(\d+)$/i;
|
|
47
36
|
/**
|
|
48
37
|
* The open issues' ids, in input order, duplicates preserved.
|
|
49
38
|
*
|
|
@@ -60,10 +49,19 @@ var ClosingReferences = class ClosingReferences extends Schema.Class("ClosingRef
|
|
|
60
49
|
/**
|
|
61
50
|
* Issue ids carried by a region's bare closing lines.
|
|
62
51
|
*
|
|
52
|
+
* @remarks
|
|
53
|
+
* The region is read with `@effected/github-references`' `parseBareLines`
|
|
54
|
+
* — per line, the whole line, after trimming, must be
|
|
55
|
+
* `<keyword>[:] #<number>`, so a number mentioned in passing is never
|
|
56
|
+
* mistaken for a closing reference. Every keyword GitHub accepts counts,
|
|
57
|
+
* not just the present-tense plural this contract emits, because a
|
|
58
|
+
* reference the parser fails to recognise is one the next regeneration
|
|
59
|
+
* silently deletes.
|
|
60
|
+
*
|
|
63
61
|
* @public
|
|
64
62
|
*/
|
|
65
63
|
static parseBare(region) {
|
|
66
|
-
return region
|
|
64
|
+
return parseBareLines(region).map((ref) => ref.issueNumber);
|
|
67
65
|
}
|
|
68
66
|
/**
|
|
69
67
|
* A copy with duplicate ids removed, first occurrence winning.
|