1+ import { LesyWorkSpace , SocketInputData , AnyObject } from "./models" ;
12import { resolve } from "path" ;
3+ import { readdirSync , existsSync } from "fs" ;
24import { createServer } from "http" ;
3- /* move inside to run method */
4- import finalhandler from "finalhandler" ;
5- import serveStatic from "serve-static" ;
6- import { default as intercept } from "intercept-stdout" ;
7- const AU = require ( "ansi_up" ) ;
8- const ansiUp = new AU . default ( ) ;
9- ansiUp . use_classes = true ;
10- ansiUp . escape_for_html = false ;
11- import { take , filter , map } from "rxjs/operators" ;
12- import { printAddressBanner } from "./helpers/banner" ;
13- import { getLocalIPAddress } from "./helpers/public-ip" ;
14-
15- export default {
16- name : "pilot" ,
17- description : "Run commands from GUI" ,
18- aliases : [ "server" , "web" , "s" , "w" ] ,
19- isVisible : false ,
20- flags : {
5+
6+ declare global {
7+ module NodeJS {
8+ interface Global {
9+ lesyWorkspace : LesyWorkSpace ;
10+ lesySelectedProject : string ;
11+ }
12+ }
13+ }
14+
15+ export default class PilotCommand {
16+ name = "pilot" ;
17+ description = "Run commands from GUI" ;
18+ aliases = [ "server" , "web" , "s" , "w" ] ;
19+ isVisible = false ;
20+ flags = {
2121 host : {
2222 aliases : [ "h" ] ,
2323 } ,
@@ -33,90 +33,194 @@ export default {
3333 clientSocketUrl : {
3434 aliases : [ "csu" ] ,
3535 } ,
36- } ,
37- run : ( { flags : props , request, feature, config, utils } ) => {
38- const flags = {
39- host : "localhost" ,
40- port : "8888" ,
41- socketHost : "localhost" ,
42- socketPort : "8889" , // todo: available ports
43- ...props ,
36+ } ;
37+ private socket : any ;
38+ private localNetworkIP : string ;
39+ private isOffline : boolean ;
40+
41+ private getBinFiles ( dir : string ) {
42+ const files = readdirSync ( dir ) ;
43+ return files
44+ . map ( ( filePath : string ) => resolve ( dir , filePath ) )
45+ . filter ( ( filePath : string ) => existsSync ( filePath ) ) ;
46+ }
47+
48+ private async fetchProjectPaths ( ) : Promise < string [ ] > {
49+ const execa = require ( "execa" ) ;
50+ const fileSearch = require ( "search-in-file" ) . fileSearch ;
51+ const { stdout : binPath } = await execa ( "npm" , [ "bin" , "-g" ] ) ;
52+ try {
53+ const results = await fileSearch (
54+ this . getBinFiles ( binPath ) ,
55+ "@lesy/compiler" ,
56+ ) ;
57+ return results ;
58+ } catch ( { message } ) {
59+ console . error ( message ) ;
60+ }
61+ }
62+
63+ private async loadProjectData ( currCtx : any ) : Promise < LesyWorkSpace > {
64+ global . lesyWorkspace = {
65+ [ currCtx . feature . pkg . name ] : currCtx ,
4466 } ;
45- const chalk = utils . color ( ) ;
46- const { "@lesy/lesy-plugin-pilot" : pilotConfig , ...allConfig } = config ;
67+ global . lesySelectedProject = currCtx . feature . pkg . name ;
68+ const projectsPaths = await this . fetchProjectPaths ( ) ;
69+ for ( let i = 0 ; i < projectsPaths . length ; i = i + 1 ) {
70+ const p = await require ( projectsPaths [ i ] ) . default ;
71+ global . lesyWorkspace [ p . feature . pkg . name ] = p . localState ;
72+ }
73+ return global . lesyWorkspace ;
74+ }
75+
76+ private getSelectedProject ( ) : any {
77+ return global . lesyWorkspace [ global . lesySelectedProject ] ;
78+ }
79+
80+ private getAllProjects ( ) : any [ ] {
81+ const projectNames = Object . keys ( global . lesyWorkspace ) ;
82+ return projectNames . map ( ( name : string ) => ( { name } ) ) ;
83+ }
84+
85+ private switchProject ( name : string ) : void {
86+ global . lesySelectedProject = name ;
87+ }
88+
89+ private getConfig ( ) : AnyObject {
90+ const { feature, config } = this . getSelectedProject ( ) ;
4791 const { name, version, bin } = feature . pkg ;
4892 const defaultPilotConfig = {
4993 appName : name || "Dashboard" ,
5094 docTitle : name || "Dashboard" ,
5195 appVersion : version || "0.0" ,
5296 cmdName : Object . keys ( bin ) [ 0 ] ,
5397 } ;
54- const socket = feature . socket
55- . startServer ( flags . socketHost , flags . socketPort )
56- . init ( {
57- requestRunCommand : request . runCommand ,
58- requestAllCommands : ( ) => ( {
59- onRequestAllCommands : request . getCommands ( ) ,
60- } ) ,
61- requestConfig : ( ) => ( {
62- onRequestConfig : {
63- pilot : { ...defaultPilotConfig , ...pilotConfig } ,
64- ...allConfig ,
65- } ,
66- } ) ,
98+ const { "@lesy/lesy-plugin-pilot" : pilotConfig , ...allConfig } = config ;
99+ return {
100+ pilot : { ...defaultPilotConfig , ...pilotConfig } ,
101+ ...allConfig ,
102+ } ;
103+ }
104+
105+ private getSocketData ( ) : SocketInputData {
106+ const selectedProject = ( ) =>
107+ this . getAllProjects ( ) . find (
108+ ( p : any ) => p . name === global . lesySelectedProject ,
109+ ) ;
110+
111+ return {
112+ requestSwitchProject : this . switchProject ,
113+ requestRunCommand : this . getSelectedProject ( ) . request . runCommand ,
114+ requestProject : ( ) => ( {
115+ onRequestProject : selectedProject ( ) ,
116+ } ) ,
117+ requestAllProjects : ( ) => ( {
118+ onRequestAllProjects : this . getAllProjects ( ) ,
119+ } ) ,
120+ requestAllCommands : ( ) => ( {
121+ onRequestAllCommands : this . getSelectedProject ( ) . request . getCommands ( ) ,
122+ } ) ,
123+ requestConfig : ( ) => ( {
124+ onRequestConfig : this . getConfig ( ) ,
125+ } ) ,
126+ } ;
127+ }
128+
129+ private tapLogs ( ) {
130+ const intercept = require ( "intercept-stdout" ) ;
131+ const AU = require ( "ansi_up" ) ;
132+ const ansiUp = new AU . default ( ) ;
133+ ansiUp . use_classes = true ;
134+ ansiUp . escape_for_html = false ;
135+
136+ const cb = ( text : any ) => {
137+ this . socket . sendMessage ( {
138+ type : "log" ,
139+ message : ansiUp . ansi_to_html ( text ) ,
67140 } ) ;
141+ } ;
68142
69- intercept (
70- ( txt : unknown ) => {
71- socket . sendMessage ( {
72- type : "log" ,
73- message : ansiUp . ansi_to_html ( txt ) ,
74- } ) ;
75- } ,
76- ( err : unknown ) => {
77- socket . sendMessage ( {
78- type : "log" ,
79- message : ansiUp . ansi_to_html ( err ) ,
80- } ) ;
81- } ,
82- ) ;
143+ intercept ( cb , cb ) ;
144+ }
83145
84- if ( feature . promptConfig ) {
85- feature . promptConfig . customPrompt = ( questions : any ) => {
86- socket . sendMessage ( { questions, messageId : 123 } ) ;
87- return new Promise ( ( res : any ) => {
88- socket
89- . listen ( )
90- . pipe (
91- take ( 1 ) ,
92- filter ( ( data : any ) => data . REQUEST === "answers" ) ,
93- map ( ( a : any ) => a . PAYLOAD ) ,
94- filter ( ( data : any ) => data . qid === 123 ) ,
95- map ( ( a : any ) => a . ans ) ,
96- )
97- . subscribe ( ( a : any ) => res ( a ) ) ; // todo: refactor
98- } ) ;
99- } ;
100- }
146+ private hijackPrompt ( feature : AnyObject ) {
147+ if ( ! feature . promptConfig ) return ;
148+ const { take, filter, map } = require ( "rxjs/operators" ) ;
149+ feature . promptConfig . customPrompt = ( questions : any ) => {
150+ this . socket . sendMessage ( { questions, messageId : 123 } ) ;
151+ return new Promise ( ( res : any ) => {
152+ this . socket
153+ . listen ( )
154+ . pipe (
155+ take ( 1 ) ,
156+ filter ( ( data : any ) => data . REQUEST === "answers" ) ,
157+ map ( ( a : any ) => a . PAYLOAD ) ,
158+ filter ( ( data : any ) => data . qid === 123 ) ,
159+ map ( ( a : any ) => a . ans ) ,
160+ )
161+ . subscribe ( ( a : any ) => res ( a ) ) ; // todo: refactor
162+ } ) ;
163+ } ;
164+ }
165+
166+ private getMergedFlags ( flags : AnyObject ) : AnyObject {
167+ const f = {
168+ host : "localhost" ,
169+ port : "8888" ,
170+ socketHost : "localhost" ,
171+ socketPort : "8889" , // todo: available ports
172+ ...flags ,
173+ } ;
174+ return f ;
175+ }
176+
177+ private establishSocket ( flags : AnyObject , feature : AnyObject ) : void {
178+ this . socket = feature . socket
179+ . startServer ( flags . socketHost , flags . socketPort )
180+ . init ( this . getSocketData ( ) ) ;
181+ }
101182
102- const localNetworkIP = getLocalIPAddress ( ) || "localhost" ;
103- const isOffline = localNetworkIP === "localhost" ;
183+ private startServer ( flags : AnyObject ) {
184+ const { getLocalIPAddress } = require ( "./helpers/public-ip" ) ;
185+ const finalhandler = require ( "finalhandler" ) ;
186+ const serveStatic = require ( "serve-static" ) ;
187+
188+ this . localNetworkIP = getLocalIPAddress ( ) || "localhost" ;
189+ this . isOffline = this . localNetworkIP === "localhost" ;
104190 const dir = resolve ( __dirname , "../public" ) ;
105191 let cookies = [ "socketHost" , "socketPort" , "clientSocketUrl" ] ;
106192 cookies = cookies . map ( ( c : any ) => `${ c } =${ flags [ c ] } ` ) ;
193+
107194 const serve = serveStatic ( dir , {
108195 index : [ "index.html" ] ,
109196 setHeaders : ( res : any ) => res . setHeader ( "Set-Cookie" , cookies ) ,
110197 } ) ;
111198 const server = createServer ( ( req , res ) => {
112199 serve ( req , res , finalhandler ( req , res ) ) ;
113200 } ) ;
201+
114202 server . listen ( flags . port , flags . host ) ;
203+ }
115204
205+ private renderConnectionInfo ( utils : AnyObject , flags : AnyObject ) {
206+ const { printAddressBanner } = require ( "./helpers/banner" ) ;
207+ const chalk = utils . color ( ) ;
116208 printAddressBanner (
117209 chalk ,
118210 `http://${ flags . host } :${ flags . port } ` ,
119- `http://${ localNetworkIP } :${ flags . port } ${ isOffline ? "(Offline)" : "" } ` ,
211+ `http://${ this . localNetworkIP } :${ flags . port } ${
212+ this . isOffline ? "(Offline)" : ""
213+ } `,
120214 ) ;
121- } ,
122- } ;
215+ }
216+
217+ async run ( { flags : f , request, feature, config, utils } ) {
218+ const flags = this . getMergedFlags ( f ) ;
219+ await this . loadProjectData ( { request, feature, config } ) ;
220+ this . tapLogs ( ) ;
221+ this . hijackPrompt ( feature ) ;
222+ this . establishSocket ( flags , feature ) ;
223+ this . startServer ( flags ) ;
224+ this . renderConnectionInfo ( utils , flags ) ;
225+ }
226+ }
0 commit comments