2019-05-14 17:11:22 +02:00
|
|
|
|
import chalk from 'chalk'
|
2019-04-10 05:15:35 +02:00
|
|
|
|
import fs from 'fs'
|
2019-05-14 17:11:22 +02:00
|
|
|
|
import textTable from 'next/dist/compiled/text-table'
|
2019-04-10 18:22:10 +02:00
|
|
|
|
import path from 'path'
|
2019-05-14 17:11:22 +02:00
|
|
|
|
import stripAnsi from 'strip-ansi'
|
2019-04-10 21:19:50 +02:00
|
|
|
|
import { promisify } from 'util'
|
2019-05-14 17:11:22 +02:00
|
|
|
|
|
2019-06-14 02:08:19 +02:00
|
|
|
|
import { isValidElementType } from 'react-is'
|
2019-05-01 22:31:08 +02:00
|
|
|
|
import prettyBytes from '../lib/pretty-bytes'
|
2019-04-10 05:15:35 +02:00
|
|
|
|
import { recursiveReadDir } from '../lib/recursive-readdir'
|
2019-05-14 17:11:22 +02:00
|
|
|
|
import { getPageChunks } from './webpack/plugins/chunk-graph-plugin'
|
2019-04-10 05:15:35 +02:00
|
|
|
|
|
2019-05-01 22:31:08 +02:00
|
|
|
|
const fsStat = promisify(fs.stat)
|
2019-04-10 21:19:50 +02:00
|
|
|
|
|
2019-04-10 05:15:35 +02:00
|
|
|
|
export function collectPages(
|
|
|
|
|
directory: string,
|
|
|
|
|
pageExtensions: string[]
|
|
|
|
|
): Promise<string[]> {
|
|
|
|
|
return recursiveReadDir(
|
|
|
|
|
directory,
|
|
|
|
|
new RegExp(`\\.(?:${pageExtensions.join('|')})$`)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-14 17:11:22 +02:00
|
|
|
|
export interface PageInfo {
|
|
|
|
|
isAmp?: boolean
|
|
|
|
|
size: number
|
|
|
|
|
chunks?: ReturnType<typeof getPageChunks>
|
2019-05-22 18:36:53 +02:00
|
|
|
|
static?: boolean
|
|
|
|
|
serverBundle: string
|
2019-05-01 22:31:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-14 17:11:22 +02:00
|
|
|
|
export function printTreeView(
|
|
|
|
|
list: string[],
|
2019-06-24 20:59:51 +02:00
|
|
|
|
pageInfos: Map<string, PageInfo>,
|
|
|
|
|
serverless: boolean
|
2019-05-14 17:11:22 +02:00
|
|
|
|
) {
|
|
|
|
|
const getPrettySize = (_size: number): string => {
|
|
|
|
|
const size = prettyBytes(_size)
|
|
|
|
|
// green for 0-100kb
|
|
|
|
|
if (_size < 100 * 1000) return chalk.green(size)
|
|
|
|
|
// yellow for 100-250kb
|
|
|
|
|
if (_size < 250 * 1000) return chalk.yellow(size)
|
|
|
|
|
// red for >= 250kb
|
|
|
|
|
return chalk.red.bold(size)
|
2019-05-01 22:31:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-14 17:11:22 +02:00
|
|
|
|
const messages: string[][] = [
|
|
|
|
|
['Page', 'Size', 'Files', 'Packages'].map(entry => chalk.underline(entry)),
|
|
|
|
|
]
|
|
|
|
|
|
2019-04-10 05:15:35 +02:00
|
|
|
|
list
|
2019-05-14 17:11:22 +02:00
|
|
|
|
.sort((a, b) => a.localeCompare(b))
|
2019-04-10 05:15:35 +02:00
|
|
|
|
.forEach((item, i) => {
|
2019-05-14 17:11:22 +02:00
|
|
|
|
const symbol =
|
2019-04-10 05:15:35 +02:00
|
|
|
|
i === 0
|
|
|
|
|
? list.length === 1
|
|
|
|
|
? '─'
|
|
|
|
|
: '┌'
|
2019-05-14 17:11:22 +02:00
|
|
|
|
: i === list.length - 1
|
|
|
|
|
? '└'
|
2019-04-10 05:15:35 +02:00
|
|
|
|
: '├'
|
2019-05-01 22:31:08 +02:00
|
|
|
|
|
2019-05-14 17:11:22 +02:00
|
|
|
|
const pageInfo = pageInfos.get(item)
|
|
|
|
|
|
|
|
|
|
messages.push([
|
2019-06-10 20:59:36 +02:00
|
|
|
|
`${symbol} ${
|
|
|
|
|
item.startsWith('/_')
|
|
|
|
|
? ' '
|
|
|
|
|
: pageInfo && pageInfo.static
|
|
|
|
|
? chalk.bold('⚡')
|
2019-06-24 20:59:51 +02:00
|
|
|
|
: serverless
|
|
|
|
|
? 'λ'
|
|
|
|
|
: 'σ'
|
2019-06-10 20:59:36 +02:00
|
|
|
|
} ${item}`,
|
2019-05-14 17:11:22 +02:00
|
|
|
|
...(pageInfo
|
|
|
|
|
? [
|
|
|
|
|
pageInfo.isAmp
|
|
|
|
|
? chalk.cyan('AMP')
|
|
|
|
|
: pageInfo.size >= 0
|
|
|
|
|
? getPrettySize(pageInfo.size)
|
2019-07-01 18:47:12 +02:00
|
|
|
|
: '',
|
|
|
|
|
pageInfo.chunks ? pageInfo.chunks.internal.size.toString() : '',
|
|
|
|
|
pageInfo.chunks ? pageInfo.chunks.external.size.toString() : '',
|
2019-05-14 17:11:22 +02:00
|
|
|
|
]
|
|
|
|
|
: ['', '', '']),
|
|
|
|
|
])
|
|
|
|
|
})
|
2019-05-01 22:31:08 +02:00
|
|
|
|
|
2019-05-14 17:11:22 +02:00
|
|
|
|
console.log(
|
|
|
|
|
textTable(messages, {
|
|
|
|
|
align: ['l', 'l', 'r', 'r'],
|
|
|
|
|
stringLength: str => stripAnsi(str).length,
|
2019-04-10 05:15:35 +02:00
|
|
|
|
})
|
2019-05-14 17:11:22 +02:00
|
|
|
|
)
|
2019-04-10 05:15:35 +02:00
|
|
|
|
|
2019-06-10 20:59:36 +02:00
|
|
|
|
console.log()
|
|
|
|
|
console.log(
|
|
|
|
|
textTable(
|
|
|
|
|
[
|
2019-06-24 20:59:51 +02:00
|
|
|
|
serverless
|
|
|
|
|
? [
|
|
|
|
|
'λ',
|
|
|
|
|
'(Lambda)',
|
|
|
|
|
`page was emitted as a lambda (i.e. ${chalk.cyan(
|
|
|
|
|
'getInitialProps'
|
|
|
|
|
)})`,
|
|
|
|
|
]
|
|
|
|
|
: [
|
|
|
|
|
'σ',
|
|
|
|
|
'(Server)',
|
|
|
|
|
`page will be server rendered (i.e. ${chalk.cyan(
|
|
|
|
|
'getInitialProps'
|
|
|
|
|
)})`,
|
|
|
|
|
],
|
2019-06-10 20:59:36 +02:00
|
|
|
|
[
|
|
|
|
|
chalk.bold('⚡'),
|
|
|
|
|
'(Static File)',
|
2019-07-02 22:51:12 +02:00
|
|
|
|
'page was prerendered as static HTML',
|
2019-06-10 20:59:36 +02:00
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
{
|
|
|
|
|
align: ['l', 'l', 'l'],
|
|
|
|
|
stringLength: str => stripAnsi(str).length,
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
2019-04-10 05:15:35 +02:00
|
|
|
|
console.log()
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-14 17:11:22 +02:00
|
|
|
|
export async function getPageSizeInKb(
|
2019-05-01 22:31:08 +02:00
|
|
|
|
page: string,
|
|
|
|
|
distPath: string,
|
2019-05-14 17:11:22 +02:00
|
|
|
|
buildId: string
|
|
|
|
|
): Promise<number> {
|
2019-05-01 22:31:08 +02:00
|
|
|
|
const clientBundle = path.join(
|
2019-05-14 17:11:22 +02:00
|
|
|
|
distPath,
|
|
|
|
|
`static/${buildId}/pages/`,
|
|
|
|
|
`${page}.js`
|
2019-05-01 22:31:08 +02:00
|
|
|
|
)
|
2019-05-14 17:11:22 +02:00
|
|
|
|
try {
|
|
|
|
|
return (await fsStat(clientBundle)).size
|
|
|
|
|
} catch (_) {}
|
|
|
|
|
return -1
|
2019-05-01 22:31:08 +02:00
|
|
|
|
}
|
2019-05-22 18:36:53 +02:00
|
|
|
|
|
|
|
|
|
export function isPageStatic(
|
|
|
|
|
serverBundle: string,
|
2019-05-29 13:57:26 +02:00
|
|
|
|
runtimeEnvConfig: any
|
2019-08-12 03:56:57 +02:00
|
|
|
|
): { static?: boolean; prerender?: boolean; isHybridAmp?: boolean } {
|
2019-05-22 18:36:53 +02:00
|
|
|
|
try {
|
2019-09-04 16:00:54 +02:00
|
|
|
|
require('../next-server/lib/runtime-config').setConfig(runtimeEnvConfig)
|
2019-07-01 23:13:52 +02:00
|
|
|
|
const mod = require(serverBundle)
|
2019-07-09 19:23:38 +02:00
|
|
|
|
const Comp = mod.default || mod
|
2019-06-25 22:36:21 +02:00
|
|
|
|
|
2019-06-14 02:08:19 +02:00
|
|
|
|
if (!Comp || !isValidElementType(Comp) || typeof Comp === 'string') {
|
2019-06-26 04:54:28 +02:00
|
|
|
|
throw new Error('INVALID_DEFAULT_EXPORT')
|
2019-05-22 18:36:53 +02:00
|
|
|
|
}
|
2019-08-12 03:56:57 +02:00
|
|
|
|
const config = mod.config || {}
|
2019-07-01 23:13:52 +02:00
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
static: typeof (Comp as any).getInitialProps !== 'function',
|
2019-08-12 03:56:57 +02:00
|
|
|
|
prerender: config.experimentalPrerender === true,
|
|
|
|
|
isHybridAmp: config.amp === 'hybrid',
|
2019-07-01 23:13:52 +02:00
|
|
|
|
}
|
2019-05-22 18:36:53 +02:00
|
|
|
|
} catch (err) {
|
2019-07-01 23:13:52 +02:00
|
|
|
|
if (err.code === 'MODULE_NOT_FOUND') return {}
|
2019-05-22 18:36:53 +02:00
|
|
|
|
throw err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function hasCustomAppGetInitialProps(
|
|
|
|
|
_appBundle: string,
|
2019-05-29 13:57:26 +02:00
|
|
|
|
runtimeEnvConfig: any
|
2019-05-22 18:36:53 +02:00
|
|
|
|
): boolean {
|
2019-09-04 16:00:54 +02:00
|
|
|
|
require('../next-server/lib/runtime-config').setConfig(runtimeEnvConfig)
|
2019-05-22 18:36:53 +02:00
|
|
|
|
let mod = require(_appBundle)
|
|
|
|
|
|
|
|
|
|
if (_appBundle.endsWith('_app.js')) {
|
2019-07-09 19:23:38 +02:00
|
|
|
|
mod = mod.default || mod
|
2019-05-22 18:36:53 +02:00
|
|
|
|
} else {
|
|
|
|
|
// since we don't output _app in serverless mode get it from a page
|
|
|
|
|
mod = mod._app
|
|
|
|
|
}
|
|
|
|
|
return mod.getInitialProps !== mod.origGetInitialProps
|
|
|
|
|
}
|