2020-11-06 03:33:14 +01:00
|
|
|
import chalk from 'chalk'
|
2020-05-15 19:30:01 +02:00
|
|
|
import stripAnsi from 'next/dist/compiled/strip-ansi'
|
2019-03-19 00:21:18 +01:00
|
|
|
import textTable from 'next/dist/compiled/text-table'
|
2019-02-27 15:12:40 +01:00
|
|
|
import createStore from 'next/dist/compiled/unistore'
|
2019-06-05 20:15:42 +02:00
|
|
|
import formatWebpackMessages from '../../client/dev/error-overlay/format-webpack-messages'
|
2019-03-19 00:21:18 +01:00
|
|
|
import { OutputState, store as consoleStore } from './store'
|
2021-10-24 23:04:26 +02:00
|
|
|
import type { webpack5 } from 'next/dist/compiled/webpack/webpack'
|
2019-02-16 17:09:49 +01:00
|
|
|
|
2021-01-25 16:13:12 +01:00
|
|
|
export function startedDevelopmentServer(appUrl: string, bindAddr: string) {
|
|
|
|
consoleStore.setState({ appUrl, bindAddr })
|
2019-02-16 17:09:49 +01:00
|
|
|
}
|
|
|
|
|
2021-10-24 23:04:26 +02:00
|
|
|
let previousClient: webpack5.Compiler | null = null
|
|
|
|
let previousServer: webpack5.Compiler | null = null
|
2021-10-26 18:50:56 +02:00
|
|
|
let previousServerWeb: webpack5.Compiler | null = null
|
2019-02-16 17:09:49 +01:00
|
|
|
|
2019-06-26 20:54:23 +02:00
|
|
|
type CompilerDiagnostics = {
|
2021-10-09 11:51:37 +02:00
|
|
|
modules: number
|
2019-06-26 20:54:23 +02:00
|
|
|
errors: string[] | null
|
|
|
|
warnings: string[] | null
|
|
|
|
}
|
|
|
|
|
2019-02-16 17:09:49 +01:00
|
|
|
type WebpackStatus =
|
|
|
|
| { loading: true }
|
2020-05-15 19:30:01 +02:00
|
|
|
| ({ loading: false } & CompilerDiagnostics)
|
2019-02-16 17:09:49 +01:00
|
|
|
|
2019-03-19 00:21:18 +01:00
|
|
|
type AmpStatus = {
|
|
|
|
message: string
|
|
|
|
line: number
|
|
|
|
col: number
|
|
|
|
specUrl: string | null
|
2019-09-03 17:11:22 +02:00
|
|
|
code: string
|
2019-03-19 00:21:18 +01:00
|
|
|
}
|
|
|
|
|
2019-10-04 17:26:44 +02:00
|
|
|
export type AmpPageStatus = {
|
2019-03-19 00:21:18 +01:00
|
|
|
[page: string]: { errors: AmpStatus[]; warnings: AmpStatus[] }
|
|
|
|
}
|
|
|
|
|
|
|
|
type BuildStatusStore = {
|
2019-02-16 17:09:49 +01:00
|
|
|
client: WebpackStatus
|
|
|
|
server: WebpackStatus
|
2021-10-26 18:50:56 +02:00
|
|
|
serverWeb?: WebpackStatus
|
2021-10-09 11:51:37 +02:00
|
|
|
trigger: string | undefined
|
2019-03-19 00:21:18 +01:00
|
|
|
amp: AmpPageStatus
|
2019-02-16 17:09:49 +01:00
|
|
|
}
|
|
|
|
|
2019-03-26 22:21:27 +01:00
|
|
|
export function formatAmpMessages(amp: AmpPageStatus) {
|
2019-03-19 00:21:18 +01:00
|
|
|
let output = chalk.bold('Amp Validation') + '\n\n'
|
|
|
|
let messages: string[][] = []
|
|
|
|
|
|
|
|
const chalkError = chalk.red('error')
|
|
|
|
function ampError(page: string, error: AmpStatus) {
|
|
|
|
messages.push([page, chalkError, error.message, error.specUrl || ''])
|
|
|
|
}
|
|
|
|
|
|
|
|
const chalkWarn = chalk.yellow('warn')
|
|
|
|
function ampWarn(page: string, warn: AmpStatus) {
|
|
|
|
messages.push([page, chalkWarn, warn.message, warn.specUrl || ''])
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const page in amp) {
|
2019-09-03 17:11:22 +02:00
|
|
|
let { errors, warnings } = amp[page]
|
|
|
|
|
|
|
|
const devOnlyFilter = (err: AmpStatus) => err.code !== 'DEV_MODE_ONLY'
|
|
|
|
errors = errors.filter(devOnlyFilter)
|
|
|
|
warnings = warnings.filter(devOnlyFilter)
|
|
|
|
if (!(errors.length || warnings.length)) {
|
|
|
|
// Skip page with no non-dev warnings
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2019-03-19 00:21:18 +01:00
|
|
|
if (errors.length) {
|
|
|
|
ampError(page, errors[0])
|
|
|
|
for (let index = 1; index < errors.length; ++index) {
|
|
|
|
ampError('', errors[index])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (warnings.length) {
|
|
|
|
ampWarn(errors.length ? '' : page, warnings[0])
|
|
|
|
for (let index = 1; index < warnings.length; ++index) {
|
|
|
|
ampWarn('', warnings[index])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
messages.push(['', '', '', ''])
|
|
|
|
}
|
|
|
|
|
2019-09-03 17:11:22 +02:00
|
|
|
if (!messages.length) {
|
|
|
|
return ''
|
|
|
|
}
|
|
|
|
|
2019-03-19 00:21:18 +01:00
|
|
|
output += textTable(messages, {
|
|
|
|
align: ['l', 'l', 'l', 'l'],
|
|
|
|
stringLength(str: string) {
|
|
|
|
return stripAnsi(str).length
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
return output
|
|
|
|
}
|
|
|
|
|
|
|
|
const buildStore = createStore<BuildStatusStore>()
|
2021-10-11 10:14:52 +02:00
|
|
|
let buildWasDone = false
|
|
|
|
let clientWasLoading = true
|
|
|
|
let serverWasLoading = true
|
2021-10-26 18:50:56 +02:00
|
|
|
let serverWebWasLoading = false
|
2019-02-16 17:09:49 +01:00
|
|
|
|
2020-05-18 21:24:37 +02:00
|
|
|
buildStore.subscribe((state) => {
|
2021-10-26 18:50:56 +02:00
|
|
|
const { amp, client, server, serverWeb, trigger } = state
|
2019-02-16 17:09:49 +01:00
|
|
|
|
2019-03-19 00:21:18 +01:00
|
|
|
const { bootstrap: bootstrapping, appUrl } = consoleStore.getState()
|
2021-10-09 11:51:37 +02:00
|
|
|
if (bootstrapping && (client.loading || server.loading)) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-10-26 18:50:56 +02:00
|
|
|
if (client.loading || server.loading || serverWeb?.loading) {
|
2021-10-09 11:51:37 +02:00
|
|
|
consoleStore.setState(
|
|
|
|
{
|
|
|
|
bootstrap: false,
|
|
|
|
appUrl: appUrl!,
|
|
|
|
loading: true,
|
|
|
|
trigger,
|
|
|
|
} as OutputState,
|
|
|
|
true
|
|
|
|
)
|
2021-10-11 10:14:52 +02:00
|
|
|
clientWasLoading = (!buildWasDone && clientWasLoading) || client.loading
|
|
|
|
serverWasLoading = (!buildWasDone && serverWasLoading) || server.loading
|
2021-10-26 18:50:56 +02:00
|
|
|
serverWebWasLoading =
|
|
|
|
(!buildWasDone && serverWasLoading) || !!serverWeb?.loading
|
2021-10-11 10:14:52 +02:00
|
|
|
buildWasDone = false
|
2019-02-16 17:09:49 +01:00
|
|
|
return
|
|
|
|
}
|
2021-10-26 18:50:56 +02:00
|
|
|
if (serverWeb?.loading) return
|
2019-02-16 17:09:49 +01:00
|
|
|
|
2021-10-11 10:14:52 +02:00
|
|
|
buildWasDone = true
|
|
|
|
|
2019-03-19 00:21:18 +01:00
|
|
|
let partialState: Partial<OutputState> = {
|
2019-02-16 17:09:49 +01:00
|
|
|
bootstrap: false,
|
|
|
|
appUrl: appUrl!,
|
2021-10-09 11:51:37 +02:00
|
|
|
loading: false,
|
|
|
|
typeChecking: false,
|
2021-10-11 10:14:52 +02:00
|
|
|
partial:
|
2021-10-26 18:50:56 +02:00
|
|
|
clientWasLoading && !serverWasLoading && !serverWebWasLoading
|
2021-10-11 10:14:52 +02:00
|
|
|
? 'client'
|
2021-10-26 18:50:56 +02:00
|
|
|
: serverWasLoading && !clientWasLoading && !serverWebWasLoading
|
2021-10-11 10:14:52 +02:00
|
|
|
? 'server'
|
2021-10-26 18:50:56 +02:00
|
|
|
: serverWebWasLoading && !clientWasLoading && !serverWasLoading
|
|
|
|
? 'serverWeb'
|
2021-10-11 10:14:52 +02:00
|
|
|
: undefined,
|
|
|
|
modules:
|
|
|
|
(clientWasLoading ? client.modules : 0) +
|
2021-10-26 18:50:56 +02:00
|
|
|
(serverWasLoading ? server.modules : 0) +
|
|
|
|
(serverWebWasLoading ? serverWeb?.modules || 0 : 0),
|
2019-02-16 17:09:49 +01:00
|
|
|
}
|
2021-10-09 11:51:37 +02:00
|
|
|
if (client.errors) {
|
|
|
|
// Show only client errors
|
2019-03-19 00:21:18 +01:00
|
|
|
consoleStore.setState(
|
2021-10-09 11:51:37 +02:00
|
|
|
{
|
|
|
|
...partialState,
|
|
|
|
errors: client.errors,
|
|
|
|
warnings: null,
|
|
|
|
} as OutputState,
|
|
|
|
true
|
|
|
|
)
|
|
|
|
} else if (server.errors) {
|
|
|
|
// Show only server errors
|
|
|
|
consoleStore.setState(
|
|
|
|
{
|
|
|
|
...partialState,
|
|
|
|
errors: server.errors,
|
|
|
|
warnings: null,
|
|
|
|
} as OutputState,
|
2019-03-19 00:21:18 +01:00
|
|
|
true
|
|
|
|
)
|
2021-10-26 18:50:56 +02:00
|
|
|
} else if (serverWeb && serverWeb.errors) {
|
|
|
|
// Show only serverWeb errors
|
|
|
|
consoleStore.setState(
|
|
|
|
{
|
|
|
|
...partialState,
|
|
|
|
errors: serverWeb.errors,
|
|
|
|
warnings: null,
|
|
|
|
} as OutputState,
|
|
|
|
true
|
|
|
|
)
|
2019-03-19 00:21:18 +01:00
|
|
|
} else {
|
2021-10-09 11:51:37 +02:00
|
|
|
// Show warnings from all of them
|
|
|
|
const warnings = [
|
|
|
|
...(client.warnings || []),
|
|
|
|
...(server.warnings || []),
|
2021-10-26 18:50:56 +02:00
|
|
|
...((serverWeb && serverWeb.warnings) || []),
|
2021-10-09 11:51:37 +02:00
|
|
|
...((Object.keys(amp).length > 0 && formatAmpMessages(amp)) || []),
|
|
|
|
]
|
2019-03-19 00:21:18 +01:00
|
|
|
|
|
|
|
consoleStore.setState(
|
2019-06-26 20:54:23 +02:00
|
|
|
{
|
|
|
|
...partialState,
|
2021-10-09 11:51:37 +02:00
|
|
|
errors: null,
|
|
|
|
warnings: warnings.length === 0 ? null : warnings,
|
2019-06-26 20:54:23 +02:00
|
|
|
} as OutputState,
|
2019-03-19 00:21:18 +01:00
|
|
|
true
|
|
|
|
)
|
|
|
|
}
|
2019-02-16 17:09:49 +01:00
|
|
|
})
|
|
|
|
|
2019-03-19 00:21:18 +01:00
|
|
|
export function ampValidation(
|
|
|
|
page: string,
|
|
|
|
errors: AmpStatus[],
|
|
|
|
warnings: AmpStatus[]
|
|
|
|
) {
|
|
|
|
const { amp } = buildStore.getState()
|
|
|
|
if (!(errors.length || warnings.length)) {
|
|
|
|
buildStore.setState({
|
|
|
|
amp: Object.keys(amp)
|
2020-05-18 21:24:37 +02:00
|
|
|
.filter((k) => k !== page)
|
2019-03-19 00:21:18 +01:00
|
|
|
.sort()
|
2019-11-11 04:24:53 +01:00
|
|
|
// eslint-disable-next-line no-sequences
|
2020-04-06 17:59:36 +02:00
|
|
|
.reduce((a, c) => ((a[c] = amp[c]), a), {} as AmpPageStatus),
|
2019-03-19 00:21:18 +01:00
|
|
|
})
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
const newAmp: AmpPageStatus = { ...amp, [page]: { errors, warnings } }
|
|
|
|
buildStore.setState({
|
|
|
|
amp: Object.keys(newAmp)
|
|
|
|
.sort()
|
2019-11-11 04:24:53 +01:00
|
|
|
// eslint-disable-next-line no-sequences
|
2020-04-06 17:59:36 +02:00
|
|
|
.reduce((a, c) => ((a[c] = newAmp[c]), a), {} as AmpPageStatus),
|
2019-03-19 00:21:18 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-06-26 20:54:23 +02:00
|
|
|
export function watchCompilers(
|
2021-10-24 23:04:26 +02:00
|
|
|
client: webpack5.Compiler,
|
2021-10-26 18:50:56 +02:00
|
|
|
server: webpack5.Compiler,
|
|
|
|
serverWeb: webpack5.Compiler
|
2019-06-26 20:54:23 +02:00
|
|
|
) {
|
2021-10-26 18:50:56 +02:00
|
|
|
if (
|
|
|
|
previousClient === client &&
|
|
|
|
previousServer === server &&
|
|
|
|
previousServerWeb === serverWeb
|
|
|
|
) {
|
2019-02-16 17:09:49 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-03-19 00:21:18 +01:00
|
|
|
buildStore.setState({
|
2019-02-16 17:09:49 +01:00
|
|
|
client: { loading: true },
|
|
|
|
server: { loading: true },
|
2021-10-26 18:50:56 +02:00
|
|
|
serverWeb: serverWeb ? { loading: true } : undefined,
|
2021-10-09 11:51:37 +02:00
|
|
|
trigger: 'initial',
|
2019-02-16 17:09:49 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
function tapCompiler(
|
|
|
|
key: string,
|
|
|
|
compiler: any,
|
|
|
|
onEvent: (status: WebpackStatus) => void
|
|
|
|
) {
|
|
|
|
compiler.hooks.invalid.tap(`NextJsInvalid-${key}`, () => {
|
|
|
|
onEvent({ loading: true })
|
|
|
|
})
|
|
|
|
|
2021-10-24 23:04:26 +02:00
|
|
|
compiler.hooks.done.tap(`NextJsDone-${key}`, (stats: webpack5.Stats) => {
|
|
|
|
buildStore.setState({ amp: {} })
|
|
|
|
|
|
|
|
const { errors, warnings } = formatWebpackMessages(
|
|
|
|
stats.toJson({
|
|
|
|
preset: 'error-warnings',
|
|
|
|
moduleTrace: true,
|
2020-04-06 17:59:36 +02:00
|
|
|
})
|
2021-10-24 23:04:26 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
const hasErrors = !!errors?.length
|
|
|
|
const hasWarnings = !!warnings?.length
|
|
|
|
|
|
|
|
onEvent({
|
|
|
|
loading: false,
|
|
|
|
modules: stats.compilation.modules.size,
|
|
|
|
errors: hasErrors ? errors : null,
|
|
|
|
warnings: hasWarnings ? warnings : null,
|
|
|
|
})
|
|
|
|
})
|
2019-02-16 17:09:49 +01:00
|
|
|
}
|
|
|
|
|
2021-10-09 11:51:37 +02:00
|
|
|
tapCompiler('client', client, (status) => {
|
|
|
|
if (!status.loading && !buildStore.getState().server.loading) {
|
|
|
|
buildStore.setState({
|
|
|
|
client: status,
|
|
|
|
trigger: undefined,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
buildStore.setState({
|
|
|
|
client: status,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
tapCompiler('server', server, (status) => {
|
|
|
|
if (!status.loading && !buildStore.getState().client.loading) {
|
|
|
|
buildStore.setState({
|
|
|
|
server: status,
|
|
|
|
trigger: undefined,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
buildStore.setState({
|
|
|
|
server: status,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
2021-10-26 18:50:56 +02:00
|
|
|
if (serverWeb) {
|
|
|
|
tapCompiler('serverWeb', serverWeb, (status) => {
|
|
|
|
buildStore.setState({
|
|
|
|
serverWeb: status,
|
|
|
|
trigger: undefined,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
2019-02-16 17:09:49 +01:00
|
|
|
|
|
|
|
previousClient = client
|
|
|
|
previousServer = server
|
2021-10-26 18:50:56 +02:00
|
|
|
previousServerWeb = serverWeb
|
2019-02-16 17:09:49 +01:00
|
|
|
}
|
2021-10-09 11:51:37 +02:00
|
|
|
|
|
|
|
export function reportTrigger(trigger: string) {
|
|
|
|
buildStore.setState({
|
|
|
|
trigger,
|
|
|
|
})
|
|
|
|
}
|