11/**
22 * This script is only used to help write release announcements.
33 */
4- // @ts -expect-error Could not find a declaration file for module
5- import { generateNotes } from "@semantic-release/release-notes-generator" ;
64import { spawn } from "node:child_process" ;
75import * as path from "node:path" ;
8- import { URL , fileURLToPath } from "node:url" ;
9- import { readJSONFile } from "../helpers.js" ;
10- import type { Manifest } from "../types.js" ;
6+ import { fileURLToPath } from "node:url" ;
117
12- function reformat (
13- output : string ,
14- lastRelease : string ,
15- nextRelease : string
16- ) : string {
17- const replacements : [ RegExp , string ] [ ] = [
18- [ / ^ # .* / m, `📣 react-native-test-app ${ nextRelease } ` ] ,
19- [ / ^ # # # .* / m, `Other fixes since ${ lastRelease } :` ] ,
20- [ / ^ \* \* \* a n d r o i d : \* \* / gm, "* **Android:**" ] ,
21- [ / ^ \* \* \* a p p l e : \* \* / gm, "* **Apple:**" ] ,
22- [ / ^ \* \* \* i o s : \* \* / gm, "* **iOS:**" ] ,
23- [ / ^ \* \* \* m a c o s : \* \* / gm, "* **macOS:**" ] ,
24- [ / ^ \* \* \* v i s i o n o s : \* \* / gm, "* **visionOS:**" ] ,
25- [ / ^ \* \* \* w i n d o w s : \* \* / gm, "* **Windows:**" ] ,
26- [ / \s * \( \[ # \d + \] \( h t t p s : \/ \/ g i t h u b .c o m .* / gm, "" ] ,
27- ] ;
28- return replacements
29- . reduce (
30- ( output , [ search , replace ] ) => output . replace ( search , replace ) ,
31- output
32- )
33- . trim ( ) ;
8+ type Commit = {
9+ hash : string ;
10+ message : string ;
11+ } ;
12+
13+ type Group =
14+ | "general"
15+ | "android"
16+ | "apple"
17+ | "ios"
18+ | "macos"
19+ | "visionos"
20+ | "windows" ;
21+
22+ type Changes = Record < string , Record < Group , string [ ] > > ;
23+
24+ function assertCategory ( category : string ) : asserts category is "feat" | "fix" {
25+ if ( category !== "feat" && category !== "fix" ) {
26+ throw new Error ( `Unknown category: ${ category } ` ) ;
27+ }
3428}
3529
36- function repositoryUrl ( ) {
37- const p = fileURLToPath ( new URL ( "../../package.json" , import . meta. url ) ) ;
38- const manifest = readJSONFile < Manifest > ( p ) ;
39- return manifest . repository ?. url ;
30+ function capitalize ( s : string ) : string {
31+ return String ( s [ 0 ] ) . toUpperCase ( ) + s . substring ( 1 ) ;
4032}
4133
42- /**
43- * @param { string } lastRelease
44- * @param { string } nextRelease
45- */
46- function main ( lastRelease : string , nextRelease : string ) : void {
34+ function getCommits (
35+ lastRelease : string ,
36+ nextRelease : string ,
37+ callback : ( commits : Commit [ ] ) => void
38+ ) : void {
4739 const args = [
4840 "log" ,
4941 `--pretty=format:{ "hash": "%H", "message": "%s" }` ,
@@ -68,33 +60,113 @@ function main(lastRelease: string, nextRelease: string): void {
6860 . replaceAll ( '"' , '\\"' )
6961 . replaceAll ( """ , '"' )
7062 . replaceAll ( "\n" , "," ) ;
63+
7164 const commits = JSON . parse ( output ) ;
7265 if ( commits . length === 0 ) {
7366 return ;
7467 }
7568
76- const context = {
77- commits,
78- lastRelease : { gitTag : lastRelease } ,
79- nextRelease : { gitTag : nextRelease } ,
80- options : {
81- repositoryUrl : repositoryUrl ( ) ,
82- } ,
83- cwd : process . cwd ( ) ,
84- } ;
85-
86- const releaseNotes : Promise < string > = generateNotes ( { } , context ) ;
87- releaseNotes
88- . then ( ( output ) => reformat ( output , lastRelease , nextRelease ) )
89- . then ( ( output ) => console . log ( output ) ) ;
69+ callback ( commits ) ;
9070 } ) ;
9171}
9272
73+ function sanitizeGroup ( group : string ) : Group {
74+ switch ( group ) {
75+ case "android" :
76+ case "apple" :
77+ case "ios" :
78+ case "macos" :
79+ case "visionos" :
80+ case "windows" :
81+ return group ;
82+ default :
83+ return "general" ;
84+ }
85+ }
86+
87+ function parseCommits ( commits : Commit [ ] ) : Changes {
88+ const changes : Changes = {
89+ feat : {
90+ general : [ ] ,
91+ android : [ ] ,
92+ apple : [ ] ,
93+ ios : [ ] ,
94+ macos : [ ] ,
95+ visionos : [ ] ,
96+ windows : [ ] ,
97+ } ,
98+ fix : {
99+ general : [ ] ,
100+ android : [ ] ,
101+ apple : [ ] ,
102+ ios : [ ] ,
103+ macos : [ ] ,
104+ visionos : [ ] ,
105+ windows : [ ] ,
106+ } ,
107+ } ;
108+
109+ for ( const { message } of commits ) {
110+ const m = message . match ( / ^ ( f e a t | f i x ) (?: \( ( .* ?) \) ) ? : ( .* ) $ / ) ;
111+ if ( m ) {
112+ const [ , cat , group , message ] = m ;
113+ assertCategory ( cat ) ;
114+ changes [ cat ] [ sanitizeGroup ( group ) ] . push ( message ) ;
115+ }
116+ }
117+
118+ return changes ;
119+ }
120+
121+ function renderGroup ( group : string ) : string {
122+ switch ( group ) {
123+ case "android" :
124+ return "**Android:** " ;
125+ case "apple" :
126+ return "**Apple:** " ;
127+ case "ios" :
128+ return "**iOS:** " ;
129+ case "macos" :
130+ return "**macOS:** " ;
131+ case "visionos" :
132+ return "**visionOS:** " ;
133+ case "windows" :
134+ return "**Windows:** " ;
135+ default :
136+ return "" ;
137+ }
138+ }
139+
140+ function renderCategory (
141+ header : string ,
142+ changes : Changes [ string ] ,
143+ output : string [ ]
144+ ) : string [ ] {
145+ const groups = Object . entries ( changes ) ;
146+ if ( groups . length > 0 ) {
147+ output . push ( "" , header , "" ) ;
148+ for ( const [ group , entries ] of groups ) {
149+ for ( const entry of entries ) {
150+ output . push ( `- ${ renderGroup ( group ) } ${ capitalize ( entry ) } ` ) ;
151+ }
152+ }
153+ }
154+ return output ;
155+ }
156+
93157const [ , , lastRelease , nextRelease ] = process . argv ;
94158if ( ! lastRelease || ! nextRelease ) {
95159 const thisScript = path . basename ( fileURLToPath ( import . meta. url ) ) ;
96160 console . log ( `Usage: ${ thisScript } <start tag> <end tag>` ) ;
97161 process . exitCode = 1 ;
98162} else {
99- main ( lastRelease , nextRelease ) ;
163+ getCommits ( lastRelease , nextRelease , ( commits ) => {
164+ const { feat, fix } = parseCommits ( commits ) ;
165+
166+ const lines = [ `📣 react-native-test-app ${ nextRelease } ` ] ;
167+ renderCategory ( "New features:" , feat , lines ) ;
168+ renderCategory ( `Fixes since ${ lastRelease } :` , fix , lines ) ;
169+
170+ console . log ( lines . join ( "\n" ) ) ;
171+ } ) ;
100172}
0 commit comments