11import path from 'node:path'
22import fs from 'node:fs/promises'
33import os from 'node:os'
4- import util , { styleText } from 'node:util'
4+ import { styleText } from 'node:util'
55
66import ansiEscapes from 'ansi-escapes'
77import dedent from 'dedent'
88import { execa } from 'execa'
99import semver from 'semver'
1010import { fileExists } from '#common'
11- import type { CommandInstallOptions , InstalledProject } from '#types'
11+ import type { CommandFixOptions , InstalledProject } from '#types'
12+ import process from 'node:process'
13+
14+ function writeStdout ( str : string ) {
15+ Deno . stdout . write ( new TextEncoder ( ) . encode ( str ) )
16+ }
1217
1318const Projects : InstalledProject [ ] = [
1419 {
@@ -82,7 +87,7 @@ const Projects: InstalledProject[] = [
8287 try {
8388 await execa ( { shell : true } ) `command -v woof`
8489 return true
85- } catch ( err ) {
90+ } catch {
8691 return false
8792 }
8893 } ,
@@ -114,27 +119,27 @@ const Ctx = {
114119 currentProject : Projects [ 0 ] . name ,
115120}
116121export function cleanupTerminal ( ) {
117- process . stdout . write ( ansiEscapes . cursorRestorePosition )
118- process . stdout . write ( ansiEscapes . cursorShow )
119- process . stdout . write ( ansiEscapes . exitAlternativeScreen )
122+ writeStdout ( ansiEscapes . cursorRestorePosition )
123+ writeStdout ( ansiEscapes . cursorShow )
124+ writeStdout ( ansiEscapes . exitAlternativeScreen )
120125}
121126let ignoreKeystrokes = false
122- export async function run ( values : CommandInstallOptions , positionals : string [ ] ) {
127+ export async function run ( options : CommandFixOptions , positionals : string [ ] ) {
123128 await fs . mkdir ( Ctx . devDir , { recursive : true } )
124129 await fs . mkdir ( Ctx . repositoryDir , { recursive : true } )
125130
126131 process . stdin . setRawMode ( true )
127- process . stdout . write ( ansiEscapes . cursorSavePosition )
128- process . stdout . write ( ansiEscapes . cursorHide )
129- process . stdout . write ( ansiEscapes . enterAlternativeScreen )
132+ writeStdout ( ansiEscapes . cursorSavePosition )
133+ writeStdout ( ansiEscapes . cursorHide )
134+ writeStdout ( ansiEscapes . enterAlternativeScreen )
130135 process . on ( 'exit' , ( ) => {
131136 if ( ! globalThis . skipTerminalCleanup ) {
132137 cleanupTerminal ( )
133138 }
134139 } )
135- process . on ( 'uncaughtException' , ( err ) => { } )
140+ process . on ( 'uncaughtException' , ( ) => { } )
136141
137- process . stdout . write ( `Fetching data...\n` )
142+ writeStdout ( `Fetching data...\n` )
138143 await updateProjectData ( )
139144 await render ( '' )
140145
@@ -164,9 +169,9 @@ async function renderMainScreen(char: string) {
164169 }
165170
166171 if ( char === '\x1B' || char === 'q' ) {
167- process . stdout . write ( ansiEscapes . cursorRestorePosition )
168- process . stdout . write ( ansiEscapes . cursorShow )
169- process . stdout . write ( ansiEscapes . exitAlternativeScreen )
172+ writeStdout ( ansiEscapes . cursorRestorePosition )
173+ writeStdout ( ansiEscapes . cursorShow )
174+ writeStdout ( ansiEscapes . exitAlternativeScreen )
170175 Deno . exit ( )
171176 } else if ( char === 'j' ) {
172177 const idx = Projects . findIndex ( ( project ) => project . name === Ctx . currentProject )
@@ -178,12 +183,12 @@ async function renderMainScreen(char: string) {
178183 Ctx . currentProject = Projects [ newIdx ] . name
179184 } else if ( char === 'c' ) {
180185 if ( project . data . isCloned ) {
181- process . stdout . write ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
182- process . stdout . write ( 'Repository already cloned...\n' )
186+ writeStdout ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
187+ writeStdout ( 'Repository already cloned...\n' )
183188 } else {
184189 const dir = path . join ( Ctx . repositoryDir , project . name )
185190
186- process . stdout . write ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
191+ writeStdout ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
187192 ignoreKeystrokes = true
188193 await execa ( {
189194 stdio : 'inherit' ,
@@ -196,16 +201,16 @@ async function renderMainScreen(char: string) {
196201 if ( project . data . isCloned ) {
197202 const dir = path . join ( Ctx . repositoryDir , project . name )
198203
199- process . stdout . write ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
200- process . stdout . write (
204+ writeStdout ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
205+ writeStdout (
201206 `REMOVING DIRECTORY: ${ path . join ( Ctx . repositoryDir , project . name ) } \n` ,
202207 )
203- process . stdout . write ( `Exit with "q/esc" to abort in less than 5 seconds\n` )
208+ writeStdout ( `Exit with "q/esc" to abort in less than 5 seconds\n` )
204209 await new Promise ( ( resolve , reject ) => {
205210 setTimeout ( async ( ) => {
206211 try {
207212 await fs . rm ( dir , { recursive : true } )
208- process . stdout . write ( 'Done. Updating project data...\n' )
213+ writeStdout ( 'Done. Updating project data...\n' )
209214 await updateProjectData ( )
210215 await waitOnConfirmInput ( )
211216 } catch ( err ) {
@@ -216,15 +221,15 @@ async function renderMainScreen(char: string) {
216221 } , 5000 )
217222 } )
218223 } else {
219- process . stdout . write ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
220- process . stdout . write ( 'Repository already removed...\n' )
224+ writeStdout ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
225+ writeStdout ( 'Repository already removed...\n' )
221226 await waitOnConfirmInput ( )
222227 }
223228 } else if ( char === 'i' ) {
224229 if ( project . data . isCloned ) {
225230 const dir = path . join ( Ctx . repositoryDir , project . name )
226231
227- process . stdout . write ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
232+ writeStdout ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
228233 const scriptFile = path . join ( os . tmpdir ( ) , `dev-${ crypto . randomUUID ( ) } .sh` )
229234 await fs . writeFile ( scriptFile , project . install )
230235 ignoreKeystrokes = true
@@ -235,15 +240,15 @@ async function renderMainScreen(char: string) {
235240 ignoreKeystrokes = false
236241 await updateProjectData ( )
237242 } else {
238- process . stdout . write ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
239- process . stdout . write ( 'Repository does not exist...\n' )
243+ writeStdout ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
244+ writeStdout ( 'Repository does not exist...\n' )
240245 }
241246 await waitOnConfirmInput ( )
242247 } else if ( char === 'u' ) {
243248 if ( project . data . isCloned ) {
244249 const dir = path . join ( Ctx . repositoryDir , project . name )
245250
246- process . stdout . write ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
251+ writeStdout ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
247252 const scriptFile = path . join ( os . tmpdir ( ) , `dev-${ crypto . randomUUID ( ) } .sh` )
248253 await fs . writeFile ( scriptFile , project . uninstall )
249254 ignoreKeystrokes = true
@@ -254,29 +259,29 @@ async function renderMainScreen(char: string) {
254259 ignoreKeystrokes = false
255260 await updateProjectData ( )
256261 } else {
257- process . stdout . write ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
258- process . stdout . write ( 'Repository does not exist...\n' )
262+ writeStdout ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
263+ writeStdout ( 'Repository does not exist...\n' )
259264 }
260265 await waitOnConfirmInput ( )
261266 } else if ( char === 'r' ) {
262- process . stdout . write ( 'LOADING...\n' )
267+ writeStdout ( 'LOADING...\n' )
263268 await updateProjectData ( )
264269 } else if ( char === 'v' ) {
265270 currentScreen = 'update-version'
266271 await render ( '' )
267272 return
268273 }
269274
270- const sep = '=' . repeat ( process . stdout . columns )
275+ const sep = '=' . repeat ( Deno . consoleSize ( ) . columns )
271276 const nameColLen = 13
272277 const clonedColLen = 8
273278 const installedColLen = 11
274279 const currentRef = 14
275280 const latestRef = 14
276281
277- process . stdout . write ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
278- process . stdout . write ( `${ sep } \n` )
279- process . stdout . write (
282+ writeStdout ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
283+ writeStdout ( `${ sep } \n` )
284+ writeStdout (
280285 ' ' +
281286 'Name' . padEnd ( nameColLen ) +
282287 'Cloned' . padEnd ( clonedColLen ) +
@@ -285,7 +290,7 @@ async function renderMainScreen(char: string) {
285290 'Latest Ref' . padEnd ( latestRef ) +
286291 '\n' ,
287292 )
288- process . stdout . write ( `${ sep } \n` )
293+ writeStdout ( `${ sep } \n` )
289294 for ( const project of Projects ) {
290295 if ( ! project ?. data ) {
291296 throw new Error ( `Project does not have data attached: \"${ project . name } \"` )
@@ -315,10 +320,10 @@ async function renderMainScreen(char: string) {
315320 str += '\n'
316321 str = str . replaceAll ( 'YES' , styleText ( 'green' , 'yes' ) )
317322 str = str . replaceAll ( 'NO' , styleText ( 'red' , 'no' ) )
318- process . stdout . write ( str )
323+ writeStdout ( str )
319324 }
320- process . stdout . write ( `${ sep } \n` )
321- process . stdout . write ( dedent `
325+ writeStdout ( `${ sep } \n` )
326+ writeStdout ( dedent `
322327 CONTROLS
323328 - j/k: Move up/down
324329 - c: Clone repository
@@ -328,7 +333,7 @@ async function renderMainScreen(char: string) {
328333 - r: Refresh refs
329334 - v: Switch version/ref of repository
330335 - q/esc: Exit program\n` )
331- process . stdout . write ( `${ sep } \n` )
336+ writeStdout ( `${ sep } \n` )
332337}
333338
334339let currentVersion = 0
@@ -346,7 +351,7 @@ async function renderUpdateVersionScreen(char: string) {
346351 currentVersion = Math . max ( 0 , currentVersion - 1 )
347352 } else if ( char === '\x0D' ) {
348353 ignoreKeystrokes = true
349- process . stdout . write ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
354+ writeStdout ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
350355 const ref = project . data . versions [ currentVersion ]
351356 const dir = path . join ( Ctx . repositoryDir , project . name )
352357 await execa `git -C ${ dir } reset --hard HEAD`
@@ -368,21 +373,21 @@ async function renderUpdateVersionScreen(char: string) {
368373 return
369374 }
370375
371- const sep = '=' . repeat ( process . stdout . columns )
372- process . stdout . write ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
373- process . stdout . write ( `REPOSITORY: ${ project . name } \n` )
376+ const sep = '=' . repeat ( Deno . consoleSize ( ) . columns )
377+ writeStdout ( ansiEscapes . eraseScreen + ansiEscapes . cursorTo ( 0 , 0 ) )
378+ writeStdout ( `REPOSITORY: ${ project . name } \n` )
374379 for ( let i = 0 ; i < project . data . versions . length ; ++ i ) {
375380 const version = project . data . versions [ i ]
376- process . stdout . write ( `[${ i === currentVersion ? 'x' : ' ' } ] ${ version } \n` )
381+ writeStdout ( `[${ i === currentVersion ? 'x' : ' ' } ] ${ version } \n` )
377382 }
378- process . stdout . write ( `${ sep } \n` )
379- process . stdout . write ( dedent `
383+ writeStdout ( `${ sep } \n` )
384+ writeStdout ( dedent `
380385 CONTROLS
381386 - j/k: Move up/down
382387 - Enter: Select version
383388 - v/backspace: Go back
384389 - q/esc: Exit program\n` )
385- process . stdout . write ( `${ sep } \n` )
390+ writeStdout ( `${ sep } \n` )
386391}
387392
388393async function updateProjectData ( ) {
@@ -449,7 +454,7 @@ async function updateProjectData() {
449454}
450455
451456async function waitOnConfirmInput ( ) {
452- process . stdout . write ( 'PRESS ENTER TO CONTINUE...\n' )
457+ writeStdout ( 'PRESS ENTER TO CONTINUE...\n' )
453458 await new Promise ( ( resolve , reject ) => {
454459 process . stdin
455460 . once ( 'data' , ( data ) => {
0 commit comments