import './node-polyfill-fetch' import { default as Server, ServerConstructor } from './next-server' import { NON_STANDARD_NODE_ENV } from '../lib/constants' import * as log from '../build/output/log' import loadConfig, { NextConfig } from './config' import { resolve } from 'path' import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_SERVER, } from '../shared/lib/constants' import { IncomingMessage, ServerResponse } from 'http' import { UrlWithParsedQuery } from 'url' type NextServerConstructor = ServerConstructor & { /** * Whether to launch Next.js in dev mode - @default false */ dev?: boolean } let ServerImpl: typeof Server const getServerImpl = async () => { if (ServerImpl === undefined) ServerImpl = (await import('./next-server')).default return ServerImpl } export class NextServer { private serverPromise?: Promise private server?: Server private reqHandlerPromise?: Promise private preparedAssetPrefix?: string options: NextServerConstructor constructor(options: NextServerConstructor) { this.options = options } getRequestHandler() { return async ( req: IncomingMessage, res: ServerResponse, parsedUrl?: UrlWithParsedQuery ) => { const requestHandler = await this.getServerRequestHandler() return requestHandler(req, res, parsedUrl) } } setAssetPrefix(assetPrefix: string) { if (this.server) { this.server.setAssetPrefix(assetPrefix) } else { this.preparedAssetPrefix = assetPrefix } } logError(...args: Parameters) { if (this.server) { this.server.logError(...args) } } async render(...args: Parameters) { const server = await this.getServer() return server.render(...args) } async renderToHTML(...args: Parameters) { const server = await this.getServer() return server.renderToHTML(...args) } async renderError(...args: Parameters) { const server = await this.getServer() return server.renderError(...args) } async renderErrorToHTML(...args: Parameters) { const server = await this.getServer() return server.renderErrorToHTML(...args) } async render404(...args: Parameters) { const server = await this.getServer() return server.render404(...args) } async serveStatic(...args: Parameters) { const server = await this.getServer() return server.serveStatic(...args) } async prepare() { const server = await this.getServer() return server.prepare() } async close() { const server = await this.getServer() return (server as any).close() } private async createServer( options: NextServerConstructor & { conf: NextConfig isNextDevCommand?: boolean } ): Promise { if (options.dev) { const DevServer = require('./dev/next-dev-server').default return new DevServer(options) } const ServerImplementation = await getServerImpl() return new ServerImplementation(options) } private async loadConfig() { const phase = this.options.dev ? PHASE_DEVELOPMENT_SERVER : PHASE_PRODUCTION_SERVER const dir = resolve(this.options.dir || '.') const conf = await loadConfig(phase, dir, this.options.conf) return conf } private async getServer() { if (!this.serverPromise) { setTimeout(getServerImpl, 10) this.serverPromise = this.loadConfig().then(async (conf) => { this.server = await this.createServer({ ...this.options, conf, }) if (this.preparedAssetPrefix) { this.server.setAssetPrefix(this.preparedAssetPrefix) } return this.server }) } return this.serverPromise } private async getServerRequestHandler() { // Memoize request handler creation if (!this.reqHandlerPromise) { this.reqHandlerPromise = this.getServer().then((server) => server.getRequestHandler().bind(server) ) } return this.reqHandlerPromise } } // This file is used for when users run `require('next')` function createServer(options: NextServerConstructor): NextServer { const standardEnv = ['production', 'development', 'test'] if (options == null) { throw new Error( 'The server has not been instantiated properly. https://nextjs.org/docs/messages/invalid-server-options' ) } if ( !(options as any).isNextDevCommand && process.env.NODE_ENV && !standardEnv.includes(process.env.NODE_ENV) ) { log.warn(NON_STANDARD_NODE_ENV) } if (options.dev) { if (typeof options.dev !== 'boolean') { console.warn( "Warning: 'dev' is not a boolean which could introduce unexpected behavior. https://nextjs.org/docs/messages/invalid-server-options" ) } } return new NextServer(options) } // Support commonjs `require('next')` module.exports = createServer exports = module.exports // Support `import next from 'next'` export default createServer