rsnext/packages/next/server/next-server.ts

137 lines
4 KiB
TypeScript
Raw Normal View History

import fs from 'fs'
import { join, relative } from 'path'
import { PAGES_MANIFEST, BUILD_ID_FILE } from '../shared/lib/constants'
import { PagesManifest } from '../build/webpack/plugins/pages-manifest-plugin'
import type { Route } from './router'
import { recursiveReadDirSync } from './lib/recursive-readdir-sync'
import { route } from './router'
import BaseServer from './base-server'
export * from './base-server'
export default class NextNodeServer extends BaseServer {
protected getHasStaticDir(): boolean {
return fs.existsSync(join(this.dir, 'static'))
}
protected getPagesManifest(): PagesManifest | undefined {
const pagesManifestPath = join(this.serverBuildDir, PAGES_MANIFEST)
return require(pagesManifestPath)
}
protected getBuildId(): string {
const buildIdFile = join(this.distDir, BUILD_ID_FILE)
try {
return fs.readFileSync(buildIdFile, 'utf8').trim()
} catch (err) {
if (!fs.existsSync(buildIdFile)) {
throw new Error(
`Could not find a production build in the '${this.distDir}' directory. Try building your app with 'next build' before starting the production server. https://nextjs.org/docs/messages/production-start-no-build-id`
)
}
throw err
}
}
protected generatePublicRoutes(): Route[] {
if (!fs.existsSync(this.publicDir)) return []
const publicFiles = new Set(
recursiveReadDirSync(this.publicDir).map((p) =>
encodeURI(p.replace(/\\/g, '/'))
)
)
return [
{
match: route('/:path*'),
name: 'public folder catchall',
fn: async (req, res, params, parsedUrl) => {
const pathParts: string[] = params.path || []
const { basePath } = this.nextConfig
// if basePath is defined require it be present
if (basePath) {
const basePathParts = basePath.split('/')
// remove first empty value
basePathParts.shift()
if (
!basePathParts.every((part: string, idx: number) => {
return part === pathParts[idx]
})
) {
return { finished: false }
}
pathParts.splice(0, basePathParts.length)
}
let path = `/${pathParts.join('/')}`
if (!publicFiles.has(path)) {
// In `next-dev-server.ts`, we ensure encoded paths match
// decoded paths on the filesystem. So we need do the
// opposite here: make sure decoded paths match encoded.
path = encodeURI(path)
}
if (publicFiles.has(path)) {
await this.serveStatic(
req,
res,
join(this.publicDir, ...pathParts),
parsedUrl
)
return {
finished: true,
}
}
return {
finished: false,
}
},
} as Route,
]
}
private _validFilesystemPathSet: Set<string> | null = null
protected getFilesystemPaths(): Set<string> {
if (this._validFilesystemPathSet) {
return this._validFilesystemPathSet
}
const pathUserFilesStatic = join(this.dir, 'static')
let userFilesStatic: string[] = []
if (this.hasStaticDir && fs.existsSync(pathUserFilesStatic)) {
userFilesStatic = recursiveReadDirSync(pathUserFilesStatic).map((f) =>
join('.', 'static', f)
)
}
let userFilesPublic: string[] = []
if (this.publicDir && fs.existsSync(this.publicDir)) {
userFilesPublic = recursiveReadDirSync(this.publicDir).map((f) =>
join('.', 'public', f)
)
}
let nextFilesStatic: string[] = []
nextFilesStatic =
!this.minimalMode && fs.existsSync(join(this.distDir, 'static'))
? recursiveReadDirSync(join(this.distDir, 'static')).map((f) =>
join('.', relative(this.dir, this.distDir), 'static', f)
)
: []
return (this._validFilesystemPathSet = new Set<string>([
...nextFilesStatic,
...userFilesPublic,
...userFilesStatic,
]))
}
}