Skip to content

Commit 3904632

Browse files
committed
fix(dev): only draw the startup QR code in a terminal
1 parent d6b96fd commit 3904632

2 files changed

Lines changed: 50 additions & 7 deletions

File tree

‎packages/nuxt-cli/src/dev/listen.ts‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import process from 'node:process'
1111

1212
import{styleText}from'node:util'
1313
import{getPort}from'get-port-please'
14+
import{isCI}from'std-env'
1415

1516
import{ActionableError}from'../utils/errors'
1617
import{debug,logger}from'../utils/logger'
@@ -340,10 +341,11 @@ export async function createListener(bound: BoundServer, options: ListenOptions
340341

341342
if(announce){
342343
if(options.showURL!==false){
343-
if(qrURL){
344-
awaitprintQRCode(qrURL)
344+
constshowQR=!!qrURL&&isQRCodeVisible()
345+
if(showQR){
346+
awaitprintQRCode(qrURL!)
345347
}
346-
showURLs({qr: !!qrURL})
348+
showURLs({qr: showQR})
347349
}
348350

349351
if(options.clipboard){
@@ -529,6 +531,15 @@ function describeBindError(error: NodeJS.ErrnoException, port: number, hostname:
529531
returnerror
530532
}
531533

534+
/**
535+
* Whether block art is worth printing. A QR code is a couple of dozen lines of
536+
* Unicode blocks that nothing downstream of a pipe can scan, so it is only
537+
* drawn where someone is looking at a terminal.
538+
*/
539+
functionisQRCodeVisible(): boolean{
540+
return!!process.stdout.isTTY&&!isCI
541+
}
542+
532543
exportasyncfunctionprintQRCode(url: string,{ showURL =false}: {showURL?: boolean}={}): Promise<void>{
533544
const{ renderUnicodeCompact }=awaitimport('uqr')
534545
constcaption=showURL ? `\n${centerBlock(styleText('cyan',url),url.length)}` : ''

‎packages/nuxt-cli/test/unit/listen.spec.ts‎

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ import { copyURL, formatDisplayURL, getNetworkAddresses, isReusePortSupported, l
1010
constwriteText=vi.hoisted(()=>vi.fn())
1111
constisolatedEnvironment=vi.hoisted(()=>({current: undefinedasstring|undefined}))
1212

13+
vi.mock('std-env',asyncimportOriginal=>({
14+
...awaitimportOriginal<typeofimport('std-env')>(),
15+
isCI: false,
16+
}))
17+
1318
vi.mock('tinyclip',()=>({ writeText }))
1419
vi.mock('../../src/dev/environment',()=>({
1520
detectIsolatedEnvironment: ()=>isolatedEnvironment.current,
@@ -234,6 +239,26 @@ describe('listen', () => {
234239
returnlistener
235240
}
236241

242+
it('should not draw a QR code into output nothing can scan it from',async()=>{
243+
constlog=vi.spyOn(console,'log').mockImplementation(()=>{})
244+
constoriginalIsTTY=process.stdout.isTTY
245+
Object.defineProperty(process.stdout,'isTTY',{value: false,configurable: true})
246+
247+
try{
248+
constlistener=awaitlisten((_req,res)=>res.end('ok'),{port: 0,hostname: '127.0.0.1',qr: true})
249+
listeners.push(listener)
250+
constprinted=log.mock.calls.map(([line])=>String(line)).join('\n')
251+
252+
expect(printed).toContain('Local:')
253+
expect(printed).not.toContain('[QR code]')
254+
expect(printed).not.toMatch(/[\u2580-\u259F]/)
255+
}
256+
finally{
257+
Object.defineProperty(process.stdout,'isTTY',{value: originalIsTTY,configurable: true})
258+
log.mockRestore()
259+
}
260+
})
261+
237262
it('should accept connections on both loopback addresses in an isolated environment',async()=>{
238263
isolatedEnvironment.current='the container'
239264
constlistener=awaitstart({port: 0})
@@ -356,12 +381,19 @@ describe('listener.close', () => {
356381
thrownewError('qr unavailable')
357382
},
358383
}))
384+
constoriginalIsTTY=process.stdout.isTTY
385+
Object.defineProperty(process.stdout,'isTTY',{value: true,configurable: true})
359386

360-
awaitexpect(listen((_req,res)=>res.end('ok'),{port: 0,hostname: '127.0.0.1',tunnel: true,qr: true})).rejects.toThrow('qr unavailable')
387+
try{
388+
awaitexpect(listen((_req,res)=>res.end('ok'),{port: 0,hostname: '127.0.0.1',tunnel: true,qr: true})).rejects.toThrow('qr unavailable')
361389

362-
expect(closeTunnel).toHaveBeenCalledTimes(1)
363-
vi.doUnmock('../../src/dev/tunnel')
364-
vi.doUnmock('uqr')
390+
expect(closeTunnel).toHaveBeenCalledTimes(1)
391+
}
392+
finally{
393+
Object.defineProperty(process.stdout,'isTTY',{value: originalIsTTY,configurable: true})
394+
vi.doUnmock('../../src/dev/tunnel')
395+
vi.doUnmock('uqr')
396+
}
365397
})
366398

367399
it('should release the port when setup fails after binding',async()=>{

0 commit comments

Comments
 (0)