2019-04-10 18:37:13 +02:00
import chalk from 'chalk'
2019-08-06 00:26:20 +02:00
import fs from 'fs'
import mkdirpOrig from 'mkdirp'
2019-04-24 11:04:36 +02:00
import {
2019-08-06 00:26:20 +02:00
PAGES_MANIFEST ,
2019-09-19 18:16:51 +02:00
BUILD_MANIFEST ,
2019-04-24 11:04:36 +02:00
PHASE_PRODUCTION_BUILD ,
2019-08-06 22:26:01 +02:00
PRERENDER_MANIFEST ,
2019-08-06 00:26:20 +02:00
SERVER_DIRECTORY ,
SERVERLESS_DIRECTORY ,
2019-09-04 16:00:54 +02:00
} from '../next-server/lib/constants'
2019-08-06 00:26:20 +02:00
import loadConfig , {
isTargetLikeServerless ,
2019-09-04 16:00:54 +02:00
} from '../next-server/server/config'
2019-04-10 18:37:13 +02:00
import nanoid from 'next/dist/compiled/nanoid/index.js'
import path from 'path'
2019-05-22 18:36:53 +02:00
import { promisify } from 'util'
2019-08-24 20:55:43 +02:00
import Worker from 'jest-worker'
2019-08-06 00:26:20 +02:00
2019-06-05 20:15:42 +02:00
import formatWebpackMessages from '../client/dev/error-overlay/format-webpack-messages'
2019-04-10 18:41:59 +02:00
import { recursiveDelete } from '../lib/recursive-delete'
2019-09-16 23:06:30 +02:00
import { recursiveReadDir } from '../lib/recursive-readdir'
2019-05-09 04:51:23 +02:00
import { verifyTypeScriptSetup } from '../lib/verifyTypeScriptSetup'
2019-08-29 18:43:06 +02:00
import {
recordBuildDuration ,
recordBuildOptimize ,
recordNextPlugins ,
recordVersion ,
} from '../telemetry/events'
2019-04-10 18:37:13 +02:00
import { CompilerResult , runCompiler } from './compiler'
import { createEntrypoints , createPagesMapping } from './entries'
2019-02-17 12:56:48 +01:00
import { generateBuildId } from './generate-build-id'
import { isWriteable } from './is-writeable'
2019-04-10 18:41:59 +02:00
import {
collectPages ,
2019-05-14 17:11:22 +02:00
getPageSizeInKb ,
2019-05-22 18:36:53 +02:00
hasCustomAppGetInitialProps ,
2019-08-06 00:26:20 +02:00
PageInfo ,
printTreeView ,
2019-04-10 18:41:59 +02:00
} from './utils'
2019-04-10 18:37:13 +02:00
import getBaseWebpackConfig from './webpack-config'
2019-08-20 17:07:57 +02:00
import { getPageChunks } from './webpack/plugins/chunk-graph-plugin'
2019-04-10 18:37:13 +02:00
import { writeBuildId } from './write-build-id'
2019-09-16 17:37:00 +02:00
import createSpinner from './spinner'
2019-09-16 23:06:30 +02:00
import { PUBLIC_DIR_MIDDLEWARE_CONFLICT } from '../lib/constants'
2019-09-24 10:50:04 +02:00
import { isDynamicRoute } from '../next-server/lib/router/utils'
2019-05-22 18:36:53 +02:00
const fsUnlink = promisify ( fs . unlink )
const fsRmdir = promisify ( fs . rmdir )
2019-09-16 23:06:30 +02:00
const fsStat = promisify ( fs . stat )
2019-05-22 18:36:53 +02:00
const fsMove = promisify ( fs . rename )
const fsReadFile = promisify ( fs . readFile )
const fsWriteFile = promisify ( fs . writeFile )
const mkdirp = promisify ( mkdirpOrig )
2019-04-06 19:11:38 +02:00
2019-09-24 10:50:04 +02:00
const staticCheckWorker = require . resolve ( './utils' )
2019-06-26 04:54:28 +02:00
2019-09-24 10:50:04 +02:00
export type SprRoute = {
initialRevalidateSeconds : number | false
}
export type PrerenderManifest = {
version : number
routes : { [ route : string ] : SprRoute }
2019-08-06 22:26:01 +02:00
}
2019-04-24 21:16:30 +02:00
export default async function build ( dir : string , conf = null ) : Promise < void > {
2019-02-17 12:56:48 +01:00
if ( ! ( await isWriteable ( dir ) ) ) {
throw new Error (
'> Build directory is not writeable. https://err.sh/zeit/next.js/build-dir-not-writeable'
)
2018-12-03 14:18:52 +01:00
}
2019-05-09 04:51:23 +02:00
await verifyTypeScriptSetup ( dir )
2019-08-29 18:43:06 +02:00
let backgroundWork : ( Promise < any > | undefined ) [ ] = [ ]
2019-09-05 02:31:35 +02:00
backgroundWork . push (
recordVersion ( { cliCommand : 'build' } ) ,
recordNextPlugins ( path . resolve ( dir ) )
)
2019-08-29 18:43:06 +02:00
2019-09-16 17:37:00 +02:00
const buildSpinner = createSpinner ( {
prefixText : 'Creating an optimized production build' ,
} )
2019-02-17 12:56:48 +01:00
2018-12-03 14:18:52 +01:00
const config = loadConfig ( PHASE_PRODUCTION_BUILD , dir , conf )
2019-06-06 15:57:42 +02:00
const { target } = config
2019-07-01 19:13:06 +02:00
const buildId = await generateBuildId ( config . generateBuildId , nanoid )
2019-04-03 02:22:04 +02:00
const distDir = path . join ( dir , config . distDir )
const pagesDir = path . join ( dir , 'pages' )
2019-09-16 23:06:30 +02:00
const publicDir = path . join ( dir , 'public' )
let publicFiles : string [ ] = [ ]
if ( config . experimental . publicDirectory ) {
publicFiles = await recursiveReadDir ( publicDir , /.*/ )
}
2019-04-03 02:22:04 +02:00
2019-08-18 21:45:39 +02:00
let tracer : any = null
if ( config . experimental . profiling ) {
const { createTrace } = require ( './profiler/profiler.js' )
tracer = createTrace ( path . join ( distDir , ` profile-events.json ` ) )
tracer . profiler . startProfiling ( )
}
2019-08-06 00:26:20 +02:00
const isLikeServerless = isTargetLikeServerless ( target )
2019-08-20 17:07:57 +02:00
const pagePaths : string [ ] = await collectPages (
pagesDir ,
config . pageExtensions
)
2019-04-10 18:41:59 +02:00
2019-05-22 18:36:53 +02:00
// needed for static exporting since we want to replace with HTML
2019-08-20 17:07:57 +02:00
// files
2019-05-22 18:36:53 +02:00
const allStaticPages = new Set < string > ( )
let allPageInfos = new Map < string , PageInfo > ( )
2019-04-10 18:41:59 +02:00
2019-03-27 16:51:05 +01:00
const mappedPages = createPagesMapping ( pagePaths , config . pageExtensions )
2019-08-20 17:07:57 +02:00
const entrypoints = createEntrypoints ( mappedPages , target , buildId , config )
2019-09-16 23:06:30 +02:00
const conflictingPublicFiles : string [ ] = [ ]
try {
await fsStat ( path . join ( publicDir , '_next' ) )
throw new Error ( PUBLIC_DIR_MIDDLEWARE_CONFLICT )
} catch ( err ) { }
for ( let file of publicFiles ) {
file = file
. replace ( /\\/g , '/' )
. replace ( /\/index$/ , '' )
. split ( publicDir )
. pop ( ) !
if ( mappedPages [ file ] ) {
conflictingPublicFiles . push ( file )
}
}
const numConflicting = conflictingPublicFiles . length
if ( numConflicting ) {
throw new Error (
` Conflicting public and page file ${
numConflicting === 1 ? ' was' : 's were'
} found . https : //err.sh/zeit/next.js/conflicting-public-file-page\n${conflictingPublicFiles.join(
'\n'
) } `
)
}
2019-03-10 15:46:50 +01:00
const configs = await Promise . all ( [
getBaseWebpackConfig ( dir , {
2019-08-18 21:45:39 +02:00
tracer ,
2019-02-17 12:56:48 +01:00
buildId ,
isServer : false ,
config ,
2019-04-24 20:32:15 +02:00
target ,
2019-02-17 12:56:48 +01:00
entrypoints : entrypoints.client ,
} ) ,
2019-03-10 15:46:50 +01:00
getBaseWebpackConfig ( dir , {
2019-08-18 21:45:39 +02:00
tracer ,
2019-02-17 12:56:48 +01:00
buildId ,
isServer : true ,
config ,
2019-04-24 20:32:15 +02:00
target ,
2019-02-17 12:56:48 +01:00
entrypoints : entrypoints.server ,
} ) ,
2019-03-10 15:46:50 +01:00
] )
2018-12-03 14:18:52 +01:00
2019-08-13 05:13:12 +02:00
const clientConfig = configs [ 0 ]
if (
clientConfig . optimization &&
( clientConfig . optimization . minimize !== true ||
( clientConfig . optimization . minimizer &&
clientConfig . optimization . minimizer . length === 0 ) )
) {
console . warn (
chalk . bold . yellow ( ` Warning: ` ) +
chalk . bold (
` Production code optimization has been disabled in your project. Read more: https://err.sh/zeit/next.js/minification-disabled `
)
)
}
2019-08-29 18:43:06 +02:00
const webpackBuildStart = process . hrtime ( )
2019-02-17 12:56:48 +01:00
let result : CompilerResult = { warnings : [ ] , errors : [ ] }
2019-08-06 00:26:20 +02:00
// TODO: why do we need this?? https://github.com/zeit/next.js/issues/8253
if ( isLikeServerless ) {
2019-08-13 05:13:12 +02:00
const clientResult = await runCompiler ( clientConfig )
2019-01-10 22:10:50 +01:00
// Fail build if clientResult contains errors
2019-02-17 12:56:48 +01:00
if ( clientResult . errors . length > 0 ) {
result = {
warnings : [ . . . clientResult . warnings ] ,
errors : [ . . . clientResult . errors ] ,
}
2019-01-10 22:10:50 +01:00
} else {
2019-03-10 15:46:50 +01:00
const serverResult = await runCompiler ( configs [ 1 ] )
2019-02-17 12:56:48 +01:00
result = {
warnings : [ . . . clientResult . warnings , . . . serverResult . warnings ] ,
errors : [ . . . clientResult . errors , . . . serverResult . errors ] ,
}
2019-01-10 22:10:50 +01:00
}
2018-12-11 21:46:23 +01:00
} else {
result = await runCompiler ( configs )
}
2019-08-29 18:43:06 +02:00
const webpackBuildEnd = process . hrtime ( webpackBuildStart )
2019-09-16 17:37:00 +02:00
if ( buildSpinner ) {
buildSpinner . stopAndPersist ( )
}
console . log ( )
2019-08-29 18:43:06 +02:00
2019-02-17 12:56:48 +01:00
result = formatWebpackMessages ( result )
2018-12-03 14:18:52 +01:00
if ( result . errors . length > 0 ) {
2019-02-17 12:56:48 +01:00
// Only keep the first error. Others are often indicative
// of the same problem, but confuse the reader with noise.
if ( result . errors . length > 1 ) {
result . errors . length = 1
}
2019-03-17 13:13:29 +01:00
const error = result . errors . join ( '\n\n' )
2019-02-17 12:56:48 +01:00
console . error ( chalk . red ( 'Failed to compile.\n' ) )
2019-07-15 17:16:35 +02:00
if (
error . indexOf ( 'private-next-pages' ) > - 1 &&
error . indexOf ( 'does not contain a default export' ) > - 1
) {
const page_name_regex = /\'private-next-pages\/(?<page_name>[^\']*)\'/
const parsed = page_name_regex . exec ( error )
const page_name = parsed && parsed . groups && parsed . groups . page_name
throw new Error (
` webpack build failed: found page without a React Component as default export in pages/ ${ page_name } \ n \ nSee https://err.sh/zeit/next.js/page-without-valid-component for more info. `
)
}
2019-03-17 13:13:29 +01:00
console . error ( error )
2019-02-17 12:56:48 +01:00
console . error ( )
2019-03-17 13:13:29 +01:00
if ( error . indexOf ( 'private-next-pages' ) > - 1 ) {
2019-03-27 16:51:05 +01:00
throw new Error (
'> webpack config.resolve.alias was incorrectly overriden. https://err.sh/zeit/next.js/invalid-resolve-alias'
)
2019-03-17 13:13:29 +01:00
}
2018-12-03 14:18:52 +01:00
throw new Error ( '> Build failed because of webpack errors' )
2019-02-17 12:56:48 +01:00
} else if ( result . warnings . length > 0 ) {
console . warn ( chalk . yellow ( 'Compiled with warnings.\n' ) )
console . warn ( result . warnings . join ( '\n\n' ) )
console . warn ( )
} else {
console . log ( chalk . green ( 'Compiled successfully.\n' ) )
2019-08-29 18:43:06 +02:00
backgroundWork . push (
recordBuildDuration ( {
2019-09-11 20:26:10 +02:00
totalPageCount : pagePaths.length ,
2019-08-29 18:43:06 +02:00
durationInSeconds : webpackBuildEnd [ 0 ] ,
} )
)
2018-12-03 14:18:52 +01:00
}
2019-01-25 18:36:29 +01:00
2019-09-16 17:37:00 +02:00
const postBuildSpinner = createSpinner ( {
prefixText : 'Automatically optimizing pages' ,
} )
2019-05-14 17:11:22 +02:00
const pageKeys = Object . keys ( mappedPages )
2019-05-29 13:57:26 +02:00
const manifestPath = path . join (
distDir ,
2019-08-06 00:26:20 +02:00
isLikeServerless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY ,
2019-05-29 13:57:26 +02:00
PAGES_MANIFEST
)
2019-09-19 18:16:51 +02:00
const buildManifestPath = path . join ( distDir , BUILD_MANIFEST )
2019-05-22 18:36:53 +02:00
2019-08-12 03:56:57 +02:00
const sprPages = new Set < string > ( )
2019-05-22 18:36:53 +02:00
const staticPages = new Set < string > ( )
2019-06-14 02:08:19 +02:00
const invalidPages = new Set < string > ( )
2019-08-12 03:56:57 +02:00
const hybridAmpPages = new Set < string > ( )
2019-09-24 10:50:04 +02:00
const additionalSprPaths = new Map < string , Array < string > > ( )
2019-05-22 18:36:53 +02:00
const pageInfos = new Map < string , PageInfo > ( )
2019-06-28 22:01:11 +02:00
const pagesManifest = JSON . parse ( await fsReadFile ( manifestPath , 'utf8' ) )
2019-09-19 18:16:51 +02:00
const buildManifest = JSON . parse ( await fsReadFile ( buildManifestPath , 'utf8' ) )
2019-05-22 18:36:53 +02:00
let customAppGetInitialProps : boolean | undefined
process . env . NEXT_PHASE = PHASE_PRODUCTION_BUILD
2019-05-01 22:31:08 +02:00
2019-08-24 20:55:43 +02:00
const staticCheckWorkers = new Worker ( staticCheckWorker , {
numWorkers : config.experimental.cpus ,
enableWorkerThreads : true ,
2019-06-26 04:54:28 +02:00
} )
2019-05-01 22:31:08 +02:00
2019-08-29 18:43:06 +02:00
const analysisBegin = process . hrtime ( )
2019-06-26 04:54:28 +02:00
await Promise . all (
pageKeys . map ( async page = > {
const chunks = getPageChunks ( page )
2019-05-22 18:36:53 +02:00
2019-06-26 04:54:28 +02:00
const actualPage = page === '/' ? '/index' : page
2019-09-19 18:16:51 +02:00
const size = await getPageSizeInKb (
actualPage ,
distDir ,
buildId ,
buildManifest ,
config . experimental . modern
)
2019-06-26 04:54:28 +02:00
const bundleRelative = path . join (
2019-08-06 00:26:20 +02:00
isLikeServerless ? 'pages' : ` static/ ${ buildId } /pages ` ,
2019-06-26 04:54:28 +02:00
actualPage + '.js'
)
const serverBundle = path . join (
2019-08-23 23:17:40 +02:00
distDir ,
2019-08-06 00:26:20 +02:00
isLikeServerless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY ,
2019-06-26 04:54:28 +02:00
bundleRelative
)
2019-06-10 20:46:30 +02:00
2019-06-26 04:54:28 +02:00
let isStatic = false
2019-05-22 18:36:53 +02:00
2019-06-28 22:01:11 +02:00
pagesManifest [ page ] = bundleRelative . replace ( /\\/g , '/' )
2019-05-22 18:36:53 +02:00
2019-06-28 22:01:11 +02:00
const runtimeEnvConfig = {
publicRuntimeConfig : config.publicRuntimeConfig ,
serverRuntimeConfig : config.serverRuntimeConfig ,
}
const nonReservedPage = ! page . match ( /^\/(_app|_error|_document|api)/ )
if ( nonReservedPage && customAppGetInitialProps === undefined ) {
customAppGetInitialProps = hasCustomAppGetInitialProps (
2019-08-06 00:26:20 +02:00
isLikeServerless
2019-06-28 22:01:11 +02:00
? serverBundle
: path . join (
2019-08-23 23:17:40 +02:00
distDir ,
2019-06-28 22:01:11 +02:00
SERVER_DIRECTORY ,
` /static/ ${ buildId } /pages/_app.js `
) ,
runtimeEnvConfig
)
if ( customAppGetInitialProps ) {
console . warn (
2019-07-05 17:57:16 +02:00
chalk . bold . yellow ( ` Warning: ` ) +
chalk . yellow (
` You have opted-out of Automatic Prerendering due to \` getInitialProps \` in \` pages/_app \` . `
)
)
console . warn (
'Read more: https://err.sh/next.js/opt-out-automatic-prerendering\n'
2019-05-29 13:57:26 +02:00
)
2019-06-28 22:01:11 +02:00
}
}
2019-06-26 04:54:28 +02:00
2019-07-01 23:13:52 +02:00
if ( nonReservedPage ) {
2019-06-28 22:01:11 +02:00
try {
2019-09-24 10:50:04 +02:00
let result : any = await ( staticCheckWorkers as any ) . isPageStatic (
page ,
2019-08-24 20:55:43 +02:00
serverBundle ,
2019-09-24 10:50:04 +02:00
runtimeEnvConfig
)
2019-05-22 18:36:53 +02:00
2019-08-12 03:56:57 +02:00
if ( result . isHybridAmp ) {
hybridAmpPages . add ( page )
}
2019-09-24 10:50:04 +02:00
if ( result . prerender ) {
sprPages . add ( page )
if ( result . prerenderRoutes ) {
additionalSprPaths . set ( page , result . prerenderRoutes )
}
}
2019-08-02 16:28:03 +02:00
if ( result . static && customAppGetInitialProps === false ) {
2019-06-28 22:01:11 +02:00
staticPages . add ( page )
isStatic = true
2019-08-06 22:26:01 +02:00
} else if ( result . prerender ) {
sprPages . add ( page )
2019-06-14 02:08:19 +02:00
}
2019-06-28 22:01:11 +02:00
} catch ( err ) {
if ( err . message !== 'INVALID_DEFAULT_EXPORT' ) throw err
invalidPages . add ( page )
2019-05-31 02:34:05 +02:00
}
2019-05-22 18:36:53 +02:00
}
2019-06-26 04:54:28 +02:00
pageInfos . set ( page , { size , chunks , serverBundle , static : isStatic } )
} )
)
2019-08-24 20:55:43 +02:00
staticCheckWorkers . end ( )
2019-04-24 10:48:43 +02:00
2019-06-14 02:08:19 +02:00
if ( invalidPages . size > 0 ) {
throw new Error (
2019-07-15 17:16:35 +02:00
` automatic static optimization failed: found page ${
2019-06-14 02:08:19 +02:00
invalidPages . size === 1 ? '' : 's'
2019-07-15 17:16:35 +02:00
} without a React Component as default export in \ n $ { [ . . . invalidPages ]
2019-06-14 02:08:19 +02:00
. map ( pg = > ` pages ${ pg } ` )
. join (
'\n'
) } \ n \ nSee https : //err.sh/zeit/next.js/page-without-valid-component for more info.\n`
)
}
2019-04-24 10:48:43 +02:00
if ( Array . isArray ( configs [ 0 ] . plugins ) ) {
configs [ 0 ] . plugins . some ( ( plugin : any ) = > {
2019-05-14 17:11:22 +02:00
if ( ! plugin . ampPages ) {
return false
2019-05-01 22:31:08 +02:00
}
2019-05-14 17:11:22 +02:00
plugin . ampPages . forEach ( ( pg : any ) = > {
pageInfos . get ( pg ) ! . isAmp = true
} )
return true
2019-04-24 10:48:43 +02:00
} )
}
2019-08-20 17:07:57 +02:00
await writeBuildId ( distDir , buildId )
2019-09-24 10:50:04 +02:00
const finalPrerenderRoutes : { [ route : string ] : SprRoute } = { }
2019-05-22 18:36:53 +02:00
2019-08-06 22:26:01 +02:00
if ( staticPages . size > 0 || sprPages . size > 0 ) {
const combinedPages = [ . . . staticPages , . . . sprPages ]
2019-05-22 18:36:53 +02:00
const exportApp = require ( '../export' ) . default
const exportOptions = {
2019-08-06 22:26:01 +02:00
sprPages ,
2019-05-22 18:36:53 +02:00
silent : true ,
buildExport : true ,
2019-08-06 22:26:01 +02:00
pages : combinedPages ,
2019-05-22 18:36:53 +02:00
outdir : path.join ( distDir , 'export' ) ,
}
2019-09-24 10:50:04 +02:00
const exportConfig : any = {
2019-05-22 18:36:53 +02:00
. . . config ,
2019-09-24 10:50:04 +02:00
initialPageRevalidationMap : { } ,
// Default map will be the collection of automatic statically exported
// pages and SPR pages.
// n.b. we cannot handle this above in combinedPages because the dynamic
// page must be in the `pages` array, but not in the mapping.
exportPathMap : ( defaultMap : any ) = > {
// Remove dynamically routed pages from the default path map. These
// pages cannot be prerendered because we don't have enough information
// to do so.
//
// Note: prerendering disables automatic static optimization.
sprPages . forEach ( page = > {
if ( isDynamicRoute ( page ) ) {
delete defaultMap [ page ]
}
} )
// Append the "well-known" routes we should prerender for, e.g. blog
// post slugs.
additionalSprPaths . forEach ( ( routes , page ) = > {
routes . forEach ( route = > {
defaultMap [ route ] = { page }
} )
} )
return defaultMap
} ,
2019-07-03 19:25:44 +02:00
exportTrailingSlash : false ,
2019-05-22 18:36:53 +02:00
}
await exportApp ( dir , exportOptions , exportConfig )
// remove server bundles that were exported
for ( const page of staticPages ) {
const { serverBundle } = pageInfos . get ( page ) !
await fsUnlink ( serverBundle )
}
2019-09-24 10:50:04 +02:00
const moveExportedPage = async (
page : string ,
file : string ,
isSpr : boolean ,
ext : 'html' | 'json'
) = > {
file = ` ${ file } . ${ ext } `
2019-05-22 18:36:53 +02:00
const orig = path . join ( exportOptions . outdir , file )
2019-08-06 00:26:20 +02:00
const relativeDest = ( isLikeServerless
2019-05-22 18:36:53 +02:00
? path . join ( 'pages' , file )
: path . join ( 'static' , buildId , 'pages' , file )
) . replace ( /\\/g , '/' )
2019-08-06 22:26:01 +02:00
const dest = path . join (
distDir ,
isLikeServerless ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY ,
relativeDest
)
2019-09-24 10:50:04 +02:00
if ( ! isSpr ) {
2019-08-06 22:26:01 +02:00
pagesManifest [ page ] = relativeDest
if ( page === '/' ) pagesManifest [ '/index' ] = relativeDest
if ( page === '/.amp' ) pagesManifest [ '/index.amp' ] = relativeDest
}
2019-05-22 18:36:53 +02:00
await mkdirp ( path . dirname ( dest ) )
await fsMove ( orig , dest )
}
2019-08-06 22:26:01 +02:00
for ( const page of combinedPages ) {
2019-09-24 10:50:04 +02:00
const isSpr = sprPages . has ( page )
const isDynamic = isDynamicRoute ( page )
2019-08-06 22:26:01 +02:00
let file = page === '/' ? '/index' : page
2019-09-24 10:50:04 +02:00
// The dynamic version of SPR pages are not prerendered. Below, we handle
// the specific prerenders of these.
if ( ! ( isSpr && isDynamic ) ) {
await moveExportedPage ( page , file , isSpr , 'html' )
}
2019-08-12 03:56:57 +02:00
const hasAmp = hybridAmpPages . has ( page )
2019-09-24 10:50:04 +02:00
if ( hasAmp ) {
await moveExportedPage ( ` ${ page } .amp ` , ` ${ file } .amp ` , isSpr , 'html' )
}
if ( isSpr ) {
// For a non-dynamic SPR page, we must copy its data file from export.
if ( ! isDynamic ) {
await moveExportedPage ( page , page , true , 'json' )
finalPrerenderRoutes [ page ] = {
initialRevalidateSeconds :
exportConfig . initialPageRevalidationMap [ page ] ,
}
} else {
// For a dynamic SPR page, we did not copy its html nor data exports.
// Instead, we must copy specific versions of this page as defined by
// `unstable_getStaticParams` (additionalSprPaths).
const extraRoutes = additionalSprPaths . get ( page ) || [ ]
for ( const route of extraRoutes ) {
await moveExportedPage ( route , route , true , 'html' )
await moveExportedPage ( route , route , true , 'json' )
finalPrerenderRoutes [ route ] = {
initialRevalidateSeconds :
exportConfig . initialPageRevalidationMap [ route ] ,
}
}
}
}
2019-08-06 22:26:01 +02:00
}
2019-05-22 18:36:53 +02:00
// remove temporary export folder
await recursiveDelete ( exportOptions . outdir )
await fsRmdir ( exportOptions . outdir )
await fsWriteFile ( manifestPath , JSON . stringify ( pagesManifest ) , 'utf8' )
}
2019-09-16 17:37:00 +02:00
if ( postBuildSpinner ) postBuildSpinner . stopAndPersist ( )
console . log ( )
2019-08-06 22:26:01 +02:00
2019-08-29 18:43:06 +02:00
const analysisEnd = process . hrtime ( analysisBegin )
backgroundWork . push (
recordBuildOptimize ( {
durationInSeconds : analysisEnd [ 0 ] ,
2019-09-11 20:26:10 +02:00
totalPageCount : pagePaths.length ,
2019-09-05 02:31:35 +02:00
staticPageCount : staticPages.size ,
2019-09-11 20:26:10 +02:00
ssrPageCount : pagePaths.length - staticPages . size ,
2019-08-29 18:43:06 +02:00
} )
)
2019-08-06 22:26:01 +02:00
if ( sprPages . size > 0 ) {
2019-09-24 10:50:04 +02:00
const prerenderManifest : PrerenderManifest = {
version : 1 ,
routes : finalPrerenderRoutes ,
}
2019-08-06 22:26:01 +02:00
await fsWriteFile (
path . join ( distDir , PRERENDER_MANIFEST ) ,
2019-09-24 10:50:04 +02:00
JSON . stringify ( prerenderManifest ) ,
2019-08-06 22:26:01 +02:00
'utf8'
)
}
2019-05-22 18:36:53 +02:00
staticPages . forEach ( pg = > allStaticPages . add ( pg ) )
pageInfos . forEach ( ( info : PageInfo , key : string ) = > {
allPageInfos . set ( key , info )
} )
2019-01-25 18:36:29 +01:00
2019-09-11 20:26:10 +02:00
printTreeView ( Object . keys ( mappedPages ) , allPageInfos , isLikeServerless )
2019-08-18 21:45:39 +02:00
if ( tracer ) {
const parsedResults = await tracer . profiler . stopProfiling ( )
await new Promise ( resolve = > {
if ( parsedResults === undefined ) {
tracer . profiler . destroy ( )
tracer . trace . flush ( )
tracer . end ( resolve )
return
}
const cpuStartTime = parsedResults . profile . startTime
const cpuEndTime = parsedResults . profile . endTime
tracer . trace . completeEvent ( {
name : 'TaskQueueManager::ProcessTaskFromWorkQueue' ,
id : ++ tracer . counter ,
cat : [ 'toplevel' ] ,
ts : cpuStartTime ,
args : {
src_file : '../../ipc/ipc_moji_bootstrap.cc' ,
src_func : 'Accept' ,
} ,
} )
tracer . trace . completeEvent ( {
name : 'EvaluateScript' ,
id : ++ tracer . counter ,
cat : [ 'devtools.timeline' ] ,
ts : cpuStartTime ,
dur : cpuEndTime - cpuStartTime ,
args : {
data : {
url : 'webpack' ,
lineNumber : 1 ,
columnNumber : 1 ,
frame : '0xFFF' ,
} ,
} ,
} )
tracer . trace . instantEvent ( {
name : 'CpuProfile' ,
id : ++ tracer . counter ,
cat : [ 'disabled-by-default-devtools.timeline' ] ,
ts : cpuEndTime ,
args : {
data : {
cpuProfile : parsedResults.profile ,
} ,
} ,
} )
tracer . profiler . destroy ( )
tracer . trace . flush ( )
tracer . end ( resolve )
} )
}
2019-08-29 18:43:06 +02:00
await Promise . all ( backgroundWork ) . catch ( ( ) = > { } )
2018-12-03 14:18:52 +01:00
}