diff --git a/packages/next/bin/next-dev.ts b/packages/next/bin/next-dev.ts index dfb64d0b..16b4ce3c 100755 --- a/packages/next/bin/next-dev.ts +++ b/packages/next/bin/next-dev.ts @@ -4,6 +4,7 @@ import arg from 'arg' import { existsSync } from 'fs' import startServer from '../server/lib/start-server' import { printAndExit } from '../server/lib/utils' +import { startedDevelopmentServer } from '../build/output' const args = arg({ // Types @@ -55,10 +56,12 @@ if (!existsSync(join(dir, 'pages'))) { } const port = args['--port'] || 3000 +const appUrl = `http://${args['--hostname'] || 'localhost'}:${port}` + +startedDevelopmentServer(appUrl) + startServer({dir, dev: true}, port, args['--hostname']) .then(async (app) => { - // tslint:disable-next-line - console.log(`> Ready on http://${args['--hostname'] || 'localhost'}:${port}`) await app.prepare() }) .catch((err) => { diff --git a/packages/next/build/output/clearConsole.ts b/packages/next/build/output/clearConsole.ts new file mode 100644 index 00000000..fadab7be --- /dev/null +++ b/packages/next/build/output/clearConsole.ts @@ -0,0 +1,13 @@ +// This file is derived from Jest: +// https://github.com/facebook/jest/blob/d9d501ac342212b8a58ddb23a31518beb7b56f47/packages/jest-util/src/specialChars.ts#L18 + +const isWindows = process.platform === 'win32' +const isInteractive = process.stdout.isTTY + +export function clearConsole() { + if (!isInteractive) { + return + } + + process.stdout.write(isWindows ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H') +} diff --git a/packages/next/build/output/index.ts b/packages/next/build/output/index.ts new file mode 100644 index 00000000..816de711 --- /dev/null +++ b/packages/next/build/output/index.ts @@ -0,0 +1,110 @@ +import createStore from 'unistore' + +import { store, OutputState } from './store' +import formatWebpackMessages from '../../client/dev-error-overlay/format-webpack-messages' + +export function startedDevelopmentServer(appUrl: string) { + store.setState({ appUrl }) +} + +let previousClient: any = null +let previousServer: any = null + +type WebpackStatus = + | { loading: true } + | { + loading: false + errors: string[] | null + warnings: string[] | null + } + +type WebpackStatusStore = { + client: WebpackStatus + server: WebpackStatus +} + +enum WebpackStatusPhase { + COMPILING = 1, + COMPILED_WITH_ERRORS = 2, + COMPILED_WITH_WARNINGS = 3, + COMPILED = 4, +} + +function getWebpackStatusPhase(status: WebpackStatus): WebpackStatusPhase { + if (status.loading) { + return WebpackStatusPhase.COMPILING + } + if (status.errors) { + return WebpackStatusPhase.COMPILED_WITH_ERRORS + } + if (status.warnings) { + return WebpackStatusPhase.COMPILED_WITH_WARNINGS + } + return WebpackStatusPhase.COMPILED +} + +const webpackStore = createStore() + +webpackStore.subscribe(state => { + const { client, server } = state + + const [{ status }] = [ + { status: client, phase: getWebpackStatusPhase(client) }, + { status: server, phase: getWebpackStatusPhase(server) }, + ].sort((a, b) => a.phase.valueOf() - b.phase.valueOf()) + + const { bootstrap: bootstrapping, appUrl } = store.getState() + if (bootstrapping && status.loading) { + return + } + + let nextStoreState: OutputState = { + bootstrap: false, + appUrl: appUrl!, + ...status, + } + store.setState(nextStoreState, true) +}) + +export function watchCompiler(client: any, server: any) { + if (previousClient === client && previousServer === server) { + return + } + + webpackStore.setState({ + client: { loading: true }, + server: { loading: true }, + }) + + function tapCompiler( + key: string, + compiler: any, + onEvent: (status: WebpackStatus) => void + ) { + compiler.hooks.invalid.tap(`NextJsInvalid-${key}`, () => { + onEvent({ loading: true }) + }) + + compiler.hooks.done.tap(`NextJsDone-${key}`, (stats: any) => { + const { errors, warnings } = formatWebpackMessages( + stats.toJson({ all: false, warnings: true, errors: true }) + ) + + onEvent({ + loading: false, + errors: errors && errors.length ? errors : null, + warnings: warnings && warnings.length ? warnings : null, + }) + }) + } + + tapCompiler('client', client, status => + webpackStore.setState({ client: status }) + ) + tapCompiler('server', server, status => + webpackStore.setState({ server: status }) + ) + + previousClient = client + previousServer = server +} diff --git a/packages/next/build/output/store.ts b/packages/next/build/output/store.ts new file mode 100644 index 00000000..b878c02d --- /dev/null +++ b/packages/next/build/output/store.ts @@ -0,0 +1,56 @@ +import chalk from 'chalk' +import createStore from 'unistore' + +import { clearConsole } from './clearConsole' + +export type OutputState = + | { bootstrap: true; appUrl: string | null } + | ({ bootstrap: false; appUrl: string | null } & ( + | { loading: true } + | { + loading: false + errors: string[] | null + warnings: string[] | null + })) + +export const store = createStore({ appUrl: null, bootstrap: true }) + +store.subscribe(state => { + clearConsole() + + if (state.bootstrap) { + console.log(chalk.cyan('Starting the development server ...')) + if (state.appUrl) { + console.log() + console.log(` > Waiting on ${state.appUrl!}`) + } + return + } + + if (state.loading) { + console.log('Compiling ...') + return + } + + if (state.errors) { + console.log(chalk.red('Failed to compile.')) + console.log() + console.log(state.errors[0]) + return + } + + if (state.warnings) { + console.log(chalk.yellow('Compiled with warnings.')) + console.log() + console.log(state.warnings.join('\n\n')) + return + } + + console.log(chalk.green('Compiled successfully!')) + if (state.appUrl) { + console.log() + console.log(` > Ready on ${state.appUrl!}`) + } + console.log() + console.log('Note that pages will be compiled when you first load them.') +}) diff --git a/packages/next/build/webpack-config.js b/packages/next/build/webpack-config.js index 737ee291..ca3fec55 100644 --- a/packages/next/build/webpack-config.js +++ b/packages/next/build/webpack-config.js @@ -2,8 +2,6 @@ import path from 'path' import webpack from 'webpack' import resolve from 'resolve' import CaseSensitivePathPlugin from 'case-sensitive-paths-webpack-plugin' -import FriendlyErrorsWebpackPlugin from 'friendly-errors-webpack-plugin' -import WebpackBar from 'webpackbar' import NextJsSsrImportPlugin from './webpack/plugins/nextjs-ssr-import' import NextJsSSRModuleCachePlugin from './webpack/plugins/nextjs-ssr-module-cache' import NextJsRequireCacheHotReloader from './webpack/plugins/nextjs-require-cache-hot-reloader' @@ -278,10 +276,6 @@ export default async function getBaseWebpackConfig (dir, {dev = false, isServer !isServer && new ReactLoadablePlugin({ filename: REACT_LOADABLE_MANIFEST }), - new WebpackBar({ - name: isServer ? 'server' : 'client' - }), - dev && !isServer && new FriendlyErrorsWebpackPlugin(), // Even though require.cache is server only we have to clear assets from both compilations // This is because the client compilation generates the build manifest that's used on the server side dev && new NextJsRequireCacheHotReloader(), diff --git a/packages/next/package.json b/packages/next/package.json index 2f55c68c..55ace5e5 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -59,13 +59,13 @@ "babel-plugin-transform-react-remove-prop-types": "0.4.15", "cacache": "^11.0.2", "case-sensitive-paths-webpack-plugin": "2.1.2", + "chalk": "2.4.2", "cross-spawn": "5.1.0", "del": "3.0.0", "event-source-polyfill": "0.0.12", "find-cache-dir": "2.0.0", "find-up": "2.1.0", "fresh": "0.5.2", - "friendly-errors-webpack-plugin": "1.7.0", "glob": "7.1.2", "hoist-non-react-statics": "3.2.0", "http-status": "1.0.1", @@ -88,12 +88,12 @@ "terser": "3.16.1", "tty-aware-progress": "1.0.3", "unfetch": "3.0.0", + "unistore": "3.2.1", "url": "0.11.0", "webpack": "4.29.0", "webpack-dev-middleware": "3.4.0", "webpack-hot-middleware": "2.24.3", "webpack-sources": "1.3.0", - "webpackbar": "3.1.4 ", "worker-farm": "1.5.2", "ws": "6.1.2" }, diff --git a/packages/next/server/hot-reloader.js b/packages/next/server/hot-reloader.js index 8e32b5ae..62430b6c 100644 --- a/packages/next/server/hot-reloader.js +++ b/packages/next/server/hot-reloader.js @@ -12,6 +12,7 @@ import {route} from 'next-server/dist/server/router' import globModule from 'glob' import {promisify} from 'util' import {createPagesMapping, createEntrypoints} from '../build/entries' +import {watchCompiler} from '../build/output' const glob = promisify(globModule) @@ -259,6 +260,11 @@ export default class HotReloader { } async prepareBuildTools (multiCompiler) { + watchCompiler( + multiCompiler.compilers[0], + multiCompiler.compilers[1] + ) + // This plugin watches for changes to _document.js and notifies the client side that it should reload the page multiCompiler.compilers[1].hooks.done.tap('NextjsHotReloaderForServer', (stats) => { if (!this.initialized) { diff --git a/yarn.lock b/yarn.lock index c1f81b24..70491161 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1898,7 +1898,7 @@ ansi-escapes@^1.0.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e" integrity sha1-06ioOzGapneTZisT52HHkRQiMG4= -ansi-escapes@^3.0.0, ansi-escapes@^3.1.0: +ansi-escapes@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" integrity sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw== @@ -2975,6 +2975,15 @@ chalk@2.4.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@2.4.2, chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -2995,15 +3004,6 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.4 escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - chardet@^0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" @@ -3109,7 +3109,7 @@ chromedriver@2.42.0: mkdirp "^0.5.1" request "^2.87.0" -ci-info@^1.5.0, ci-info@^1.6.0: +ci-info@^1.5.0: version "1.6.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497" integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A== @@ -3388,17 +3388,6 @@ configstore@3.1.2: write-file-atomic "^2.0.0" xdg-basedir "^3.0.0" -consola@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/consola/-/consola-2.3.0.tgz#256b80a927234351d512a4e5693eac9cf8a572b9" - integrity sha512-gsawoQfj4DtnwsaPrabFpFOZBxWpzpT+E9fu6YAdFKO3NvBOOsFcQl/cskDOoIDDLMkLZvm4jjMWvSEelIumIw== - dependencies: - chalk "^2.4.1" - dayjs "^1.7.7" - figures "^2.0.0" - std-env "^2.2.1" - string-width "^2.1.1" - console-browserify@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" @@ -3978,11 +3967,6 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -dayjs@^1.7.7: - version "1.7.8" - resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.7.8.tgz#05d288f8d4b2140110cc1519cfe317d6f1f11a3c" - integrity sha512-Gp4Y5KWeSri0QOWGzHQz7VrKDkfEpS92dCLK7P8hYowRFbaym1vj3d6CoHio3apSS4KSi/qb5Edemv26IN5Hfg== - debug-log@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/debug-log/-/debug-log-1.0.1.tgz#2307632d4c04382b8df8a32f70b895046d52745f" @@ -4458,13 +4442,6 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -error-stack-parser@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.2.tgz#4ae8dbaa2bf90a8b450707b9149dcabca135520d" - integrity sha512-E1fPutRDdIj/hohG0UpT5mayXNCxXP9d+snxFsPU9X0XgccOumKraa3juDMwTUyi7+Bu5+mCGagjg4IYeNbOdw== - dependencies: - stackframe "^1.0.4" - es-abstract@^1.5.1, es-abstract@^1.6.1, es-abstract@^1.7.0: version "1.12.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.12.0.tgz#9dbbdd27c6856f0001421ca18782d786bf8a6165" @@ -5365,15 +5342,6 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= -friendly-errors-webpack-plugin@1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.0.tgz#efc86cbb816224565861a1be7a9d84d0aafea136" - integrity sha512-K27M3VK30wVoOarP651zDmb93R9zF28usW4ocaK3mfQeIEI5BPht/EzZs5E8QLLwbLRJQMwscAjDxYPb1FuNiw== - dependencies: - chalk "^1.1.3" - error-stack-parser "^2.0.0" - string-width "^2.0.0" - from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" @@ -9610,11 +9578,6 @@ pretty-format@^23.6.0: ansi-regex "^3.0.0" ansi-styles "^3.2.0" -pretty-time@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" - integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== - private@^0.1.6, private@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -11128,11 +11091,6 @@ stack-utils@^1.0.1: resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" integrity sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA== -stackframe@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.0.4.tgz#357b24a992f9427cba6b545d96a14ed2cbca187b" - integrity sha512-to7oADIniaYwS3MhtCa/sQhrxidCCQiF/qp4/m5iN3ipf0Y7Xlri0f6eG29r08aL7JYl8n32AF3Q5GYBZ7K8vw== - staged-git-files@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/staged-git-files/-/staged-git-files-0.0.4.tgz#d797e1b551ca7a639dec0237dc6eb4bb9be17d35" @@ -11186,13 +11144,6 @@ statuses@~1.4.0: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== -std-env@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/std-env/-/std-env-2.2.1.tgz#2ffa0fdc9e2263e0004c1211966e960948a40f6b" - integrity sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ== - dependencies: - ci-info "^1.6.0" - stdout-stream@^1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de" @@ -11979,6 +11930,11 @@ unique-string@^1.0.0: dependencies: crypto-random-string "^1.0.0" +unistore@3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/unistore/-/unistore-3.2.1.tgz#26e316c834e39b83b2a301a65f78138f43975a0b" + integrity sha512-102VTsu5dcoADsz+NdBE55JeFh1YYLvrEzLenwE+nu3lFWSVZj2MpvXLkBqrx/YTk+L3kzMG5UwHkHoj/9VcaQ== + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -12296,20 +12252,6 @@ webpack@4.29.0: watchpack "^1.5.0" webpack-sources "^1.3.0" -"webpackbar@3.1.4 ": - version "3.1.4" - resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-3.1.4.tgz#7b99fd28bf7c8d4f890b14c042418fc56d0877f0" - integrity sha512-P/ESpzVFl49IL9svoZphf9Kbyh/09vHqo31PP5/fxVrBLCBUHMKbDaWt+Px7zEQZUyFuQCWzRASJHZByQHTdKw== - dependencies: - ansi-escapes "^3.1.0" - chalk "^2.4.1" - consola "^2.3.0" - figures "^2.0.0" - pretty-time "^1.1.0" - std-env "^2.2.1" - text-table "^0.2.0" - wrap-ansi "^4.0.0" - whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" @@ -12404,15 +12346,6 @@ wrap-ansi@^2.0.0: string-width "^1.0.1" strip-ansi "^3.0.1" -wrap-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-4.0.0.tgz#b3570d7c70156159a2d42be5cc942e957f7b1131" - integrity sha512-uMTsj9rDb0/7kk1PbcbCcwvHUxp60fGDB/NNXpVa0Q+ic/e7y5+BwTxKfQ33VYgDppSwi/FBzpetYzo8s6tfbg== - dependencies: - ansi-styles "^3.2.0" - string-width "^2.1.1" - strip-ansi "^4.0.0" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"