1
1
import path from 'path'
2
2
import { argv } from 'process'
3
+ import crypto from 'crypto'
3
4
import fs from 'fs-extra'
4
5
import c from 'picocolors'
5
6
import prompts from 'prompts'
6
7
import { formatToCode } from './actions/utils/formatToCode'
7
8
import { loadQuizes , resolveInfo } from './loader'
8
- import { defaultLocale , supportedLocales } from './locales'
9
+ import { supportedLocales } from './locales'
9
10
import { getQuestionFullName } from './actions/issue-pr'
10
11
import type { QuizMetaInfo } from './types'
11
12
13
+ type Snapshot = Record < string , string >
14
+
15
+ function calculateFileHash ( filePathFull : string ) : Promise < string > {
16
+ return new Promise ( ( resolve , reject ) => {
17
+ const hash = crypto . createHash ( 'sha1' )
18
+ const fileStream = fs . createReadStream ( filePathFull )
19
+
20
+ fileStream . on ( 'data' , ( data ) => {
21
+ hash . update ( data )
22
+ } )
23
+
24
+ fileStream . on ( 'end' , ( ) => {
25
+ hash . update ( filePathFull )
26
+ resolve ( hash . digest ( 'hex' ) )
27
+ } )
28
+
29
+ fileStream . on ( 'error' , ( err ) => {
30
+ reject ( err )
31
+ } )
32
+ } )
33
+ }
34
+
35
+ async function takeSnapshot ( quizesPath : string ) {
36
+ let snapshot : Snapshot = { }
37
+
38
+ const files = fs . readdirSync ( quizesPath )
39
+
40
+ for ( const file of files ) {
41
+ // Might be a file, or a folder
42
+ const fPath = path . join ( quizesPath , file )
43
+ const fStats = fs . statSync ( fPath )
44
+
45
+ if ( fStats . isDirectory ( ) ) {
46
+ snapshot = {
47
+ ...snapshot ,
48
+ ...( await takeSnapshot ( fPath ) ) ,
49
+ }
50
+ }
51
+ else {
52
+ snapshot [ file ] = await calculateFileHash ( fPath )
53
+ }
54
+ }
55
+
56
+ return snapshot
57
+ }
58
+
59
+ function readPlaygroundCache ( playgroundCachePath : string ) : Snapshot {
60
+ if ( ! fs . existsSync ( playgroundCachePath ) )
61
+ return { }
62
+
63
+ try {
64
+ const rawCacheContent = fs . readFileSync ( playgroundCachePath )
65
+ return JSON . parse ( rawCacheContent . toString ( ) )
66
+ }
67
+ catch ( err ) {
68
+ console . log ( c . red ( 'Playground cache corrupted. '
69
+ + 'Cannot generate playground without keeping your changes intact' ) )
70
+ console . log ( c . cyan ( 'Please ensure you have run this: "pnpm generate"' ) )
71
+ process . exit ( 1 )
72
+ }
73
+ }
74
+
75
+ function calculateOverridableFiles ( cache : Snapshot , snapshot : Snapshot ) {
76
+ const result : Snapshot = { }
77
+
78
+ for ( const quizName in snapshot ) {
79
+ if ( snapshot [ quizName ] === cache [ quizName ] )
80
+ result [ quizName ] = snapshot [ quizName ]
81
+ }
82
+
83
+ return result
84
+ }
85
+
12
86
async function generatePlayground ( ) {
13
- const quizesPath = path . join ( __dirname , '../playground' )
87
+ const playgroundPath = path . join ( __dirname , '../playground' )
88
+ const playgroundCachePath = path . join ( __dirname , '../.playgroundcache' )
89
+
14
90
let locale = supportedLocales . find ( locale => locale === argv [ 2 ] ) !
15
91
16
- console . log ( c . bold ( c . cyan ( 'Generateing local playground...\n' ) ) )
92
+ console . log ( c . bold ( c . cyan ( 'Generating local playground...\n' ) ) )
93
+
94
+ let overridableFiles : Snapshot = { }
95
+ let keepChanges = false
96
+ const currentPlaygroundCache = readPlaygroundCache ( playgroundCachePath )
17
97
18
- if ( fs . existsSync ( quizesPath ) ) {
98
+ if ( argv . length === 3 && ( argv [ 2 ] === '--keep-changes' || argv [ 2 ] === '-K' ) ) {
99
+ console . log ( c . bold ( c . cyan ( 'We will keep your chanegs while generating.\n' ) ) )
100
+ keepChanges = true
101
+
102
+ const playgroundSnapshot = await takeSnapshot ( playgroundPath )
103
+
104
+ overridableFiles = calculateOverridableFiles ( currentPlaygroundCache , playgroundSnapshot )
105
+ }
106
+ else if ( fs . existsSync ( playgroundPath ) ) {
19
107
const result = await prompts ( [ {
20
108
name : 'confirm' ,
21
109
type : 'confirm' ,
@@ -41,20 +129,38 @@ async function generatePlayground() {
41
129
locale = result . locale
42
130
}
43
131
44
- await fs . remove ( quizesPath )
45
- await fs . ensureDir ( quizesPath )
132
+ if ( ! keepChanges ) {
133
+ await fs . remove ( playgroundPath )
134
+ await fs . ensureDir ( playgroundPath )
135
+ }
46
136
47
137
const quizes = await loadQuizes ( )
138
+ const incomingQuizesCache : Snapshot = { }
139
+
48
140
for ( const quiz of quizes ) {
49
141
const { difficulty, title } = resolveInfo ( quiz , locale ) as QuizMetaInfo & { difficulty : string }
50
142
const code = formatToCode ( quiz , locale )
51
- const filepath = path . join ( quizesPath , `${ getQuestionFullName ( quiz . no , difficulty , title ) } .ts` )
52
143
53
- await fs . writeFile ( filepath , code , 'utf-8' )
144
+ const quizesPathByDifficulty = path . join ( playgroundPath , difficulty )
145
+
146
+ const quizFileName = `${ getQuestionFullName ( quiz . no , difficulty , title ) } .ts`
147
+ const quizPathFull = path . join ( quizesPathByDifficulty , quizFileName )
148
+
149
+ if ( ! keepChanges || overridableFiles [ quizFileName ] ) {
150
+ if ( ! fs . existsSync ( quizesPathByDifficulty ) )
151
+ fs . mkdirSync ( quizesPathByDifficulty )
152
+ await fs . writeFile ( quizPathFull , code , 'utf-8' )
153
+ incomingQuizesCache [ quizFileName ] = await calculateFileHash ( quizPathFull )
154
+ }
54
155
}
55
156
157
+ fs . writeFile ( playgroundCachePath , JSON . stringify ( {
158
+ ...currentPlaygroundCache ,
159
+ ...incomingQuizesCache ,
160
+ } ) )
161
+
56
162
console . log ( )
57
- console . log ( c . bold ( c . green ( 'Local playground generated at: ' ) ) + c . dim ( quizesPath ) )
163
+ console . log ( c . bold ( c . green ( 'Local playground generated at: ' ) ) + c . dim ( playgroundPath ) )
58
164
console . log ( )
59
165
}
60
166
0 commit comments