1
1
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-assignment */
2
2
import readline from 'node:readline' ;
3
+ import { isProxy } from 'node:util/types' ;
3
4
import {
4
5
defer ,
5
- EMPTY ,
6
6
from ,
7
7
of ,
8
8
concatMap ,
9
9
filter ,
10
10
reduce ,
11
11
isObservable ,
12
12
Observable ,
13
+ Subject ,
13
14
lastValueFrom ,
15
+ tap ,
14
16
} from 'rxjs' ;
15
17
import runAsync from 'run-async' ;
16
18
import MuteStream from 'mute-stream' ;
@@ -40,11 +42,7 @@ export const _ = {
40
42
pointer = pointer [ key ] as Record < string , unknown > ;
41
43
} ) ;
42
44
} ,
43
- get : (
44
- obj : object ,
45
- path : string | number | symbol = '' ,
46
- defaultValue ?: unknown ,
47
- ) : any => {
45
+ get : ( obj : object , path : string = '' , defaultValue ?: unknown ) : any => {
48
46
const travel = ( regexp : RegExp ) =>
49
47
String . prototype . split
50
48
. call ( path , regexp )
@@ -193,23 +191,43 @@ function isPromptConstructor(
193
191
*/
194
192
export default class PromptsRunner < A extends Answers > {
195
193
private prompts : PromptCollection ;
196
- answers : Partial < A > = { } ;
197
- process : Observable < any > = EMPTY ;
194
+ answers : Partial < A > ;
195
+ process : Subject < { name : string ; answer : any } > = new Subject ( ) ;
198
196
private abortController : AbortController = new AbortController ( ) ;
199
197
private opt : StreamOptions ;
200
198
rl ?: InquirerReadline ;
201
199
202
- constructor ( prompts : PromptCollection , opt : StreamOptions = { } ) {
200
+ constructor (
201
+ prompts : PromptCollection ,
202
+ { answers = { } , ...opt } : StreamOptions & { answers ?: Partial < A > } = { } ,
203
+ ) {
203
204
this . opt = opt ;
204
205
this . prompts = prompts ;
206
+
207
+ this . answers = isProxy ( answers )
208
+ ? answers
209
+ : new Proxy (
210
+ { ...answers } ,
211
+ {
212
+ get : ( target , prop ) => {
213
+ if ( typeof prop !== 'string' ) {
214
+ return ;
215
+ }
216
+ return _ . get ( target , prop ) ;
217
+ } ,
218
+ set : ( target , prop : string , value ) => {
219
+ _ . set ( target , prop , value ) ;
220
+ return true ;
221
+ } ,
222
+ } ,
223
+ ) ;
205
224
}
206
225
207
- async run ( questions : PromptSession < A > , answers ?: Partial < A > ) : Promise < A > {
226
+ async run < Session extends PromptSession < A > = PromptSession < A > > (
227
+ questions : Session ,
228
+ ) : Promise < A > {
208
229
this . abortController = new AbortController ( ) ;
209
230
210
- // Keep global reference to the answers
211
- this . answers = typeof answers === 'object' ? { ...answers } : { } ;
212
-
213
231
let obs : Observable < AnyQuestion < A > > ;
214
232
if ( isQuestionArray ( questions ) ) {
215
233
obs = from ( questions ) ;
@@ -224,34 +242,36 @@ export default class PromptsRunner<A extends Answers> {
224
242
) ;
225
243
} else {
226
244
// Case: Called with a single question config
227
- obs = from ( [ questions ] ) ;
245
+ obs = from ( [ questions as AnyQuestion < A > ] ) ;
228
246
}
229
247
230
- this . process = obs . pipe (
231
- concatMap ( ( question ) =>
232
- of ( question ) . pipe (
248
+ return lastValueFrom (
249
+ obs
250
+ . pipe (
233
251
concatMap ( ( question ) =>
234
- from (
235
- this . shouldRun ( question ) . then ( ( shouldRun : boolean | void ) => {
236
- if ( shouldRun ) {
237
- return question ;
238
- }
239
- return ;
240
- } ) ,
241
- ) . pipe ( filter ( ( val ) => val != null ) ) ,
252
+ of ( question )
253
+ . pipe (
254
+ concatMap ( ( question ) =>
255
+ from (
256
+ this . shouldRun ( question ) . then ( ( shouldRun : boolean | void ) => {
257
+ if ( shouldRun ) {
258
+ return question ;
259
+ }
260
+ return ;
261
+ } ) ,
262
+ ) . pipe ( filter ( ( val ) => val != null ) ) ,
263
+ ) ,
264
+ concatMap ( ( question ) => defer ( ( ) => from ( this . fetchAnswer ( question ) ) ) ) ,
265
+ )
266
+ . pipe ( tap ( ( answer ) => this . process . next ( answer ) ) ) ,
242
267
) ,
243
- concatMap ( ( question ) => defer ( ( ) => from ( this . fetchAnswer ( question ) ) ) ) ,
268
+ )
269
+ . pipe (
270
+ reduce ( ( answersObj : any , answer : { name : string ; answer : unknown } ) => {
271
+ answersObj [ answer . name ] = answer . answer ;
272
+ return answersObj ;
273
+ } , this . answers ) ,
244
274
) ,
245
- ) ,
246
- ) ;
247
-
248
- return lastValueFrom (
249
- this . process . pipe (
250
- reduce ( ( answersObj , answer : { name : string ; answer : unknown } ) => {
251
- _ . set ( answersObj , answer . name , answer . answer ) ;
252
- return answersObj ;
253
- } , this . answers ) ,
254
- ) ,
255
275
)
256
276
. then ( ( ) => this . answers as A )
257
277
. finally ( ( ) => this . close ( ) ) ;
@@ -389,10 +409,7 @@ export default class PromptsRunner<A extends Answers> {
389
409
} ;
390
410
391
411
private shouldRun = async ( question : AnyQuestion < A > ) : Promise < boolean > => {
392
- if (
393
- question . askAnswered !== true &&
394
- _ . get ( this . answers , question . name ) !== undefined
395
- ) {
412
+ if ( question . askAnswered !== true && this . answers [ question . name ] !== undefined ) {
396
413
return false ;
397
414
}
398
415
0 commit comments