@@ -11,6 +11,101 @@ const LAST_RUN_PATH = new URL("./last-success.isodate", import.meta.url)
1111 . pathname
1212const CODE_DIR = new URL ( "../../src/code" , import . meta. url ) . pathname
1313
14+ type ConflictSide = "ours" | "theirs"
15+
16+ function resolveLastRunConflict (
17+ content : string ,
18+ ) : { value : string ; side : ConflictSide } | null {
19+ if ( ! content . includes ( "<<<<<<<" ) ) {
20+ return null
21+ }
22+
23+ const ours : string [ ] = [ ]
24+ const theirs : string [ ] = [ ]
25+ let mode : "normal" | ConflictSide = "normal"
26+
27+ for ( const line of content . split ( "\n" ) ) {
28+ if ( line . startsWith ( "<<<<<<<" ) ) {
29+ mode = "ours"
30+ continue
31+ }
32+
33+ if ( line . startsWith ( "=======" ) ) {
34+ mode = "theirs"
35+ continue
36+ }
37+
38+ if ( line . startsWith ( ">>>>>>>" ) ) {
39+ mode = "normal"
40+ continue
41+ }
42+
43+ if ( mode === "ours" ) {
44+ ours . push ( line )
45+ } else if ( mode === "theirs" ) {
46+ theirs . push ( line )
47+ }
48+ }
49+
50+ const candidates = [
51+ { side : "ours" as const , value : ours . join ( "\n" ) . trim ( ) } ,
52+ { side : "theirs" as const , value : theirs . join ( "\n" ) . trim ( ) } ,
53+ ] . map ( candidate => {
54+ const time = Date . parse ( candidate . value )
55+ return { ...candidate , time }
56+ } )
57+
58+ const validCandidates = candidates . filter ( candidate =>
59+ Number . isFinite ( candidate . time ) ,
60+ )
61+
62+ if ( validCandidates . length === 0 ) {
63+ return null
64+ }
65+
66+ validCandidates . sort ( ( a , b ) => b . time - a . time )
67+ const [ latest ] = validCandidates
68+
69+ return { value : latest . value , side : latest . side }
70+ }
71+
72+ function resolveStatsConflict (
73+ content : string ,
74+ preferred : ConflictSide ,
75+ ) : string {
76+ if ( ! content . includes ( "<<<<<<<" ) ) {
77+ return content
78+ }
79+
80+ const resolved : string [ ] = [ ]
81+ let mode : "normal" | ConflictSide = "normal"
82+
83+ for ( const line of content . split ( "\n" ) ) {
84+ if ( line . startsWith ( "<<<<<<<" ) ) {
85+ mode = "ours"
86+ continue
87+ }
88+
89+ if ( line . startsWith ( "=======" ) ) {
90+ mode = "theirs"
91+ continue
92+ }
93+
94+ if ( line . startsWith ( ">>>>>>>" ) ) {
95+ mode = "normal"
96+ continue
97+ }
98+
99+ if ( mode === "normal" || mode === preferred ) {
100+ resolved . push ( line )
101+ }
102+ }
103+
104+ const needsTrailingNewline = content . endsWith ( "\n" )
105+ const joined = resolved . join ( "\n" )
106+ return needsTrailingNewline ? `${ joined } \n` : joined
107+ }
108+
14109async function main ( ) {
15110 const filePaths = await fg ( "./**/*.md" , { cwd : CODE_DIR , absolute : true } )
16111
@@ -19,7 +114,30 @@ async function main() {
19114 {
20115 // we only sync once every two hours
21116 const TWO_HOURS = 2 * 60 * 60 * 1000
22- const lastRun = await fs . readFile ( LAST_RUN_PATH , "utf8" ) . catch ( ( ) => "" )
117+ const lastRunRaw = await fs . readFile ( LAST_RUN_PATH , "utf8" ) . catch ( ( ) => "" )
118+
119+ const conflictResolution = resolveLastRunConflict ( lastRunRaw )
120+ const lastRun = conflictResolution
121+ ? conflictResolution . value
122+ : lastRunRaw . trim ( )
123+
124+ if ( conflictResolution ) {
125+ await fs . writeFile ( LAST_RUN_PATH , lastRun )
126+ const statsContent = await fs . readFile ( DATA_PATH , "utf8" )
127+ if ( statsContent . includes ( "<<<<<<<" ) ) {
128+ console . log (
129+ "Resolved conflict. Picked" ,
130+ conflictResolution . side ,
131+ "from" ,
132+ lastRun ,
133+ )
134+ await fs . writeFile (
135+ DATA_PATH ,
136+ resolveStatsConflict ( statsContent , conflictResolution . side ) ,
137+ )
138+ }
139+ }
140+
23141 const twoHoursAgo = new Date ( Date . now ( ) - TWO_HOURS )
24142 if ( lastRun && new Date ( lastRun ) . getTime ( ) > twoHoursAgo . getTime ( ) ) {
25143 console . info (
@@ -40,6 +158,7 @@ async function main() {
40158 const content = await fs . readFile ( filePath , "utf8" )
41159 const { data } = grayMatter ( content )
42160 if ( data . github ) {
161+ // TODO: This needs to be pooled to make the builds faster.
43162 const stats = await getGitHubStats ( data . github )
44163 if ( stats ) {
45164 newState . set ( data . github , stats )
0 commit comments