Do not clear the console (#6758)
* Do not clear the console Its rude to clear the console, you may be sharing output with other processes even in tty mode. * Remove unused dependency * Dedupe and cleanup dev output without clearing * use logError * Remove exit handler * Add next helper * Add log helpers * Switch store to log helpers and a shallow object compare * Update other files to use new logging utility * request => build * Update ready on messages * Use case insensitive matching
This commit is contained in:
parent
1c305ab36e
commit
52dd42a6bb
10 changed files with 84 additions and 83 deletions
|
@ -22,7 +22,8 @@
|
||||||
"prepublish": "lerna run prepublish",
|
"prepublish": "lerna run prepublish",
|
||||||
"publish-canary": "lerna version prerelease --preid canary --force-publish && release --pre",
|
"publish-canary": "lerna version prerelease --preid canary --force-publish && release --pre",
|
||||||
"publish-stable": "lerna version --force-publish",
|
"publish-stable": "lerna version --force-publish",
|
||||||
"lint-staged": "lint-staged"
|
"lint-staged": "lint-staged",
|
||||||
|
"next": "./packages/next/dist/bin/next"
|
||||||
},
|
},
|
||||||
"pre-commit": "lint-staged",
|
"pre-commit": "lint-staged",
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
export function onExit(fn: Function) {
|
|
||||||
function exit(signal: string = '') {
|
|
||||||
try {
|
|
||||||
if (listeners.length) {
|
|
||||||
fn()
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
while (listeners.length) {
|
|
||||||
const { event, handler } = listeners.shift()!
|
|
||||||
process.removeListener(event, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (signal) {
|
|
||||||
process.kill(process.pid, signal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const listeners = [
|
|
||||||
{ event: 'SIGINT', handler: () => exit('SIGINT') },
|
|
||||||
{ event: 'SIGHUP', handler: () => exit('SIGHUP') },
|
|
||||||
{ event: 'SIGQUIT', handler: () => exit('SIGQUIT') },
|
|
||||||
{ event: 'SIGTERM', handler: () => exit('SIGTERM') },
|
|
||||||
|
|
||||||
{
|
|
||||||
event: 'uncaughtException',
|
|
||||||
handler: (error: Error) => {
|
|
||||||
console.error('Got uncaughtException:', error)
|
|
||||||
exit()
|
|
||||||
process.exit(1)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ event: 'exit', handler: () => exit() },
|
|
||||||
]
|
|
||||||
|
|
||||||
for (const { event, handler } of listeners) {
|
|
||||||
process.on(event as any, handler)
|
|
||||||
}
|
|
||||||
}
|
|
34
packages/next/build/output/log.ts
Normal file
34
packages/next/build/output/log.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import chalk from 'chalk'
|
||||||
|
|
||||||
|
const prefixes = {
|
||||||
|
wait: chalk`[ {cyan wait} ] `,
|
||||||
|
error: chalk`[ {red error} ]`,
|
||||||
|
warn: chalk`[ {yellow warn} ] `,
|
||||||
|
ready: chalk`[ {green ready} ]`,
|
||||||
|
info: chalk`[ {cyan {dim info}} ] `,
|
||||||
|
event: chalk`[ {magenta event} ]`,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function wait(...message: string[]) {
|
||||||
|
console.log(prefixes.wait, ...message)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function error(...message: string[]) {
|
||||||
|
console.log(prefixes.error, ...message)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function warn(...message: string[]) {
|
||||||
|
console.log(prefixes.warn, ...message)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ready(...message: string[]) {
|
||||||
|
console.log(prefixes.ready, ...message)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function info(...message: string[]) {
|
||||||
|
console.log(prefixes.info, ...message)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function event(...message: string[]) {
|
||||||
|
console.log(prefixes.event, ...message)
|
||||||
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
import chalk from 'chalk'
|
|
||||||
import createStore from 'next/dist/compiled/unistore'
|
import createStore from 'next/dist/compiled/unistore'
|
||||||
import readline from 'readline'
|
|
||||||
import { onExit } from './exit'
|
|
||||||
import stripAnsi from 'strip-ansi'
|
import stripAnsi from 'strip-ansi'
|
||||||
|
|
||||||
|
import * as Log from './log'
|
||||||
|
|
||||||
export type OutputState =
|
export type OutputState =
|
||||||
| { bootstrap: true; appUrl: string | null }
|
| { bootstrap: true; appUrl: string | null }
|
||||||
| ({ bootstrap: false; appUrl: string | null } & (
|
| ({ bootstrap: false; appUrl: string | null } & (
|
||||||
|
@ -16,62 +15,66 @@ export type OutputState =
|
||||||
|
|
||||||
export const store = createStore<OutputState>({ appUrl: null, bootstrap: true })
|
export const store = createStore<OutputState>({ appUrl: null, bootstrap: true })
|
||||||
|
|
||||||
process.stdout.write('\n'.repeat(process.stdout.rows || 1))
|
let lastStore: OutputState = {} as any
|
||||||
process.stdout.write('\u001b[?25l')
|
function hasStoreChanged(nextStore: OutputState) {
|
||||||
onExit(() => {
|
if (
|
||||||
process.stdout.write('\u001b[?25h')
|
[...new Set([...Object.keys(lastStore), ...Object.keys(nextStore)])].every(
|
||||||
})
|
key => Object.is((lastStore as any)[key], (nextStore as any)[key])
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
lastStore = nextStore
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
store.subscribe(state => {
|
store.subscribe(state => {
|
||||||
if (process.stdout.isTTY) {
|
if (!hasStoreChanged(state)) {
|
||||||
readline.cursorTo(process.stdout, 0, 0)
|
return
|
||||||
readline.clearScreenDown(process.stdout)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.bootstrap) {
|
if (state.bootstrap) {
|
||||||
console.log(chalk.cyan('Starting the development server ...'))
|
Log.wait('starting the development server ...')
|
||||||
if (state.appUrl) {
|
Log.info(`waiting on ${state.appUrl!} ...`)
|
||||||
console.log()
|
|
||||||
console.log(` > Waiting on ${state.appUrl!}`)
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.loading) {
|
if (state.loading) {
|
||||||
console.log('Compiling ...')
|
Log.wait('compiling ...')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.errors) {
|
if (state.errors) {
|
||||||
console.log(chalk.red('Failed to compile.'))
|
Log.error(state.errors[0])
|
||||||
console.log()
|
|
||||||
const cleanError = stripAnsi(state.errors[0])
|
const cleanError = stripAnsi(state.errors[0])
|
||||||
if (cleanError.indexOf('SyntaxError') > -1) {
|
if (cleanError.indexOf('SyntaxError') > -1) {
|
||||||
const matches = cleanError.match(/\[.*\]=/)
|
const matches = cleanError.match(/\[.*\]=/)
|
||||||
if (matches) {
|
if (matches) {
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
const prop = (match.split(']').shift() || '').substr(1)
|
const prop = (match.split(']').shift() || '').substr(1)
|
||||||
console.log(`AMP bind syntax [${prop}]='' is not supported in JSX, use 'data-amp-bind-${prop}' instead. https://err.sh/zeit/next.js/amp-bind-jsx-alt`)
|
console.log(
|
||||||
|
`AMP bind syntax [${prop}]='' is not supported in JSX, use 'data-amp-bind-${prop}' instead. https://err.sh/zeit/next.js/amp-bind-jsx-alt`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
console.log()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
console.log(state.errors[0])
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.warnings) {
|
if (state.warnings) {
|
||||||
console.log(chalk.yellow('Compiled with warnings.'))
|
Log.warn(state.warnings.join('\n\n'))
|
||||||
console.log()
|
Log.info(`ready on ${state.appUrl!}`)
|
||||||
console.log(state.warnings.join('\n\n'))
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(chalk.green('Compiled successfully!'))
|
|
||||||
if (state.appUrl) {
|
if (state.appUrl) {
|
||||||
console.log()
|
Log.ready('compiled successfully')
|
||||||
console.log(` > Ready on ${state.appUrl!}`)
|
if (state.appUrl) {
|
||||||
|
Log.info(`ready on ${state.appUrl!}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
console.log()
|
|
||||||
console.log('Note that pages will be compiled when you first load them.')
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { PHASE_DEVELOPMENT_SERVER } from 'next-server/constants'
|
||||||
import ErrorDebug from './error-debug'
|
import ErrorDebug from './error-debug'
|
||||||
import AmpHtmlValidator from 'amphtml-validator'
|
import AmpHtmlValidator from 'amphtml-validator'
|
||||||
import { ampValidation } from '../build/output/index'
|
import { ampValidation } from '../build/output/index'
|
||||||
|
import * as Log from '../build/output/log'
|
||||||
|
|
||||||
const React = require('react')
|
const React = require('react')
|
||||||
|
|
||||||
|
@ -176,7 +177,7 @@ export default class DevServer extends Server {
|
||||||
const out = await super.renderErrorToHTML(err, req, res, pathname, query)
|
const out = await super.renderErrorToHTML(err, req, res, pathname, query)
|
||||||
return out
|
return out
|
||||||
} catch (err2) {
|
} catch (err2) {
|
||||||
if (!this.quiet) console.error(err2)
|
if (!this.quiet) Log.error(err2)
|
||||||
res.statusCode = 500
|
res.statusCode = 500
|
||||||
return super.renderErrorToHTML(err2, req, res, pathname, query)
|
return super.renderErrorToHTML(err2, req, res, pathname, query)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { ROUTE_NAME_REGEX, IS_BUNDLED_PAGE_REGEX } from 'next-server/constants'
|
||||||
import { stringify } from 'querystring'
|
import { stringify } from 'querystring'
|
||||||
import { findPageFile } from './lib/find-page-file'
|
import { findPageFile } from './lib/find-page-file'
|
||||||
import { isWriteable } from '../build/is-writeable'
|
import { isWriteable } from '../build/is-writeable'
|
||||||
|
import * as Log from '../build/output/log'
|
||||||
|
|
||||||
const ADDED = Symbol('added')
|
const ADDED = Symbol('added')
|
||||||
const BUILDING = Symbol('building')
|
const BUILDING = Symbol('building')
|
||||||
|
@ -51,6 +52,7 @@ export default function onDemandEntryHandler (devMiddleware, multiCompiler, {
|
||||||
let reloading = false
|
let reloading = false
|
||||||
let stopped = false
|
let stopped = false
|
||||||
let reloadCallbacks = new EventEmitter()
|
let reloadCallbacks = new EventEmitter()
|
||||||
|
let lastEntry = null
|
||||||
|
|
||||||
for (const compiler of compilers) {
|
for (const compiler of compilers) {
|
||||||
compiler.hooks.make.tapPromise('NextJsOnDemandEntries', (compilation) => {
|
compiler.hooks.make.tapPromise('NextJsOnDemandEntries', (compilation) => {
|
||||||
|
@ -60,7 +62,7 @@ export default function onDemandEntryHandler (devMiddleware, multiCompiler, {
|
||||||
const { name, absolutePagePath } = entries[page]
|
const { name, absolutePagePath } = entries[page]
|
||||||
const pageExists = await isWriteable(absolutePagePath)
|
const pageExists = await isWriteable(absolutePagePath)
|
||||||
if (!pageExists) {
|
if (!pageExists) {
|
||||||
console.warn('Page was removed', page)
|
Log.event('page was removed', page)
|
||||||
delete entries[page]
|
delete entries[page]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -164,11 +166,10 @@ export default function onDemandEntryHandler (devMiddleware, multiCompiler, {
|
||||||
const entryInfo = entries[page]
|
const entryInfo = entries[page]
|
||||||
let toSend
|
let toSend
|
||||||
|
|
||||||
// If there's no entry.
|
// If there's no entry, it may have been invalidated and needs to be re-built.
|
||||||
// Then it seems like an weird issue.
|
|
||||||
if (!entryInfo) {
|
if (!entryInfo) {
|
||||||
const message = `Client pings, but there's no entry for page: ${page}`
|
if (page !== lastEntry) Log.event(`client pings, but there's no entry for page: ${page}`)
|
||||||
console.error(message)
|
lastEntry = page
|
||||||
return { invalid: true }
|
return { invalid: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -257,7 +258,7 @@ export default function onDemandEntryHandler (devMiddleware, multiCompiler, {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`> Building page: ${normalizedPage}`)
|
Log.event(`build page: ${normalizedPage}`)
|
||||||
|
|
||||||
entries[normalizedPage] = { name, absolutePagePath, status: ADDED }
|
entries[normalizedPage] = { name, absolutePagePath, status: ADDED }
|
||||||
doneCallbacks.once(normalizedPage, handleCallback)
|
doneCallbacks.once(normalizedPage, handleCallback)
|
||||||
|
@ -364,7 +365,7 @@ function disposeInactiveEntries (devMiddleware, entries, lastAccessPages, maxIna
|
||||||
disposingPages.forEach((page) => {
|
disposingPages.forEach((page) => {
|
||||||
delete entries[page]
|
delete entries[page]
|
||||||
})
|
})
|
||||||
console.log(`> Disposing inactive page(s): ${disposingPages.join(', ')}`)
|
Log.event(`disposing inactive page(s): ${disposingPages.join(', ')}`)
|
||||||
devMiddleware.invalidate()
|
devMiddleware.invalidate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ describe('CLI Usage', () => {
|
||||||
test('custom directory', async () => {
|
test('custom directory', async () => {
|
||||||
const port = await findPort()
|
const port = await findPort()
|
||||||
const output = await runNextCommandDev([dir, '--port', port], true)
|
const output = await runNextCommandDev([dir, '--port', port], true)
|
||||||
expect(output).toMatch(/Ready on/)
|
expect(output).toMatch(/ready on/i)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('--port', async () => {
|
test('--port', async () => {
|
||||||
|
|
|
@ -32,7 +32,7 @@ const startServer = async (optEnv = {}) => {
|
||||||
optEnv
|
optEnv
|
||||||
)
|
)
|
||||||
|
|
||||||
server = await initNextServerScript(scriptPath, /Ready on/, env, /ReferenceError: options is not defined/)
|
server = await initNextServerScript(scriptPath, /ready on/i, env, /ReferenceError: options is not defined/)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Custom Server', () => {
|
describe('Custom Server', () => {
|
||||||
|
|
|
@ -26,7 +26,7 @@ const startServer = async (optEnv = {}) => {
|
||||||
optEnv
|
optEnv
|
||||||
)
|
)
|
||||||
|
|
||||||
server = await initNextServerScript(scriptPath, /Ready on/, env)
|
server = await initNextServerScript(scriptPath, /ready on/i, env)
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('FileSystemPublicRoutes', () => {
|
describe('FileSystemPublicRoutes', () => {
|
||||||
|
|
|
@ -123,7 +123,7 @@ export function runNextCommandDev (argv, stdOut) {
|
||||||
|
|
||||||
function handleStdout (data) {
|
function handleStdout (data) {
|
||||||
const message = data.toString()
|
const message = data.toString()
|
||||||
if (/> Ready on/.test(message)) {
|
if (/ready on/i.test(message)) {
|
||||||
resolve(stdOut ? message : instance)
|
resolve(stdOut ? message : instance)
|
||||||
}
|
}
|
||||||
process.stdout.write(message)
|
process.stdout.write(message)
|
||||||
|
|
Loading…
Reference in a new issue