Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add handling for custom-routes with basePath #15041

Merged
merged 3 commits into from Jul 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/next/build/index.ts
Expand Up @@ -259,11 +259,22 @@ export default async function build(
const buildCustomRoute = (
r: {
source: string
basePath?: false
statusCode?: number
destination?: string
},
type: RouteType
) => {
const keys: any[] = []

if (r.basePath !== false) {
r.source = `${config.basePath}${r.source}`

if (r.destination && r.destination.startsWith('/')) {
r.destination = `${config.basePath}${r.destination}`
}
}

const routeRegex = pathToRegexp(r.source, keys, {
strict: true,
sensitive: false,
Expand Down
13 changes: 8 additions & 5 deletions packages/next/build/webpack/loaders/next-serverless-loader.ts
Expand Up @@ -137,7 +137,9 @@ const nextServerlessLoader: loader.Loader = function () {
const { parsedDestination } = prepareDestination(
rewrite.destination,
params,
parsedUrl.query
parsedUrl.query,
true,
"${basePath}"
)

Object.assign(parsedUrl.query, parsedDestination.query, params)
Expand Down Expand Up @@ -173,6 +175,7 @@ const nextServerlessLoader: loader.Loader = function () {
? `
// always strip the basePath if configured since it is required
req.url = req.url.replace(new RegExp('^${basePath}'), '') || '/'
parsedUrl.pathname = parsedUrl.pathname.replace(new RegExp('^${basePath}'), '') || '/'
`
: ''

Expand Down Expand Up @@ -204,13 +207,13 @@ const nextServerlessLoader: loader.Loader = function () {
try {
await initServer()

${handleBasePath}

// We need to trust the dynamic route params from the proxy
// to ensure we are using the correct values
const trustQuery = req.headers['${vercelHeader}']
const parsedUrl = handleRewrites(parse(req.url, true))

${handleBasePath}

const params = ${
pageIsDynamicRoute
? `
Expand Down Expand Up @@ -296,8 +299,6 @@ const nextServerlessLoader: loader.Loader = function () {
export async function renderReqToHTML(req, res, renderMode, _renderOpts, _params) {
const fromExport = renderMode === 'export' || renderMode === true;

${handleBasePath}

const options = {
App,
Document,
Expand All @@ -324,6 +325,8 @@ const nextServerlessLoader: loader.Loader = function () {
const trustQuery = !getStaticProps && req.headers['${vercelHeader}']
const parsedUrl = handleRewrites(parse(req.url, true))

${handleBasePath}

if (parsedUrl.pathname.match(/_next\\/data/)) {
_nextData = true
parsedUrl.pathname = parsedUrl.pathname
Expand Down
9 changes: 8 additions & 1 deletion packages/next/lib/load-custom-routes.ts
Expand Up @@ -8,6 +8,7 @@ import {
export type Rewrite = {
source: string
destination: string
basePath?: false
}

export type Redirect = Rewrite & {
Expand All @@ -17,6 +18,7 @@ export type Redirect = Rewrite & {

export type Header = {
source: string
basePath?: false
headers: Array<{ key: string; value: string }>
}

Expand Down Expand Up @@ -148,10 +150,11 @@ function checkCustomRoutes(
allowedKeys = new Set([
'source',
'destination',
'basePath',
...(isRedirect ? ['statusCode', 'permanent'] : []),
])
} else {
allowedKeys = new Set(['source', 'headers'])
allowedKeys = new Set(['source', 'headers', 'basePath'])
}

for (const route of routes) {
Expand All @@ -171,6 +174,10 @@ function checkCustomRoutes(
const invalidKeys = keys.filter((key) => !allowedKeys.has(key))
const invalidParts: string[] = []

if (typeof route.basePath !== 'undefined' && route.basePath !== false) {
invalidParts.push('`basePath` must be undefined or false')
}

if (!route.source) {
invalidParts.push('`source` is missing')
} else if (typeof route.source !== 'string') {
Expand Down
41 changes: 29 additions & 12 deletions packages/next/next-server/server/next-server.ts
Expand Up @@ -253,13 +253,11 @@ export default class Server {

const { basePath } = this.nextConfig

// if basePath is set require it be present
if (basePath && !req.url!.startsWith(basePath)) {
return this.render404(req, res, parsedUrl)
} else {
// If replace ends up replacing the full url it'll be `undefined`, meaning we have to default it to `/`
parsedUrl.pathname = parsedUrl.pathname!.replace(basePath, '') || '/'
req.url = req.url!.replace(basePath, '')
if (basePath && req.url?.startsWith(basePath)) {
// store original URL to allow checking if basePath was
// provided or not
;(req as any)._nextHadBasePath = true
req.url = req.url!.replace(basePath, '') || '/'
Copy link

@nodkz nodkz Nov 24, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ijjk @timneutkens what about adding req.baseUrl = basePath; to express req object in this code block?

I have a problem that I cannot make a proper redirect in getInitialProps() with basePath. I have no ability to read it from ctx or ctx.req object on the server-side for proper redirect URL construction.

Or providing basePath to ctx can be solved in another way?

}

res.statusCode = 200
Expand Down Expand Up @@ -308,6 +306,7 @@ export default class Server {
}

protected generateRoutes(): {
basePath: string
headers: Route[]
rewrites: Route[]
fsRoutes: Route[]
Expand Down Expand Up @@ -445,11 +444,17 @@ export default class Server {
...staticFilesRoute,
]

const getCustomRouteBasePath = (r: { basePath?: false }) => {
return r.basePath !== false && this.renderOpts.dev
? this.nextConfig.basePath
: ''
}

const getCustomRoute = (r: Rewrite | Redirect | Header, type: RouteType) =>
({
...r,
type,
match: getCustomRouteMatcher(r.source),
match: getCustomRouteMatcher(`${getCustomRouteBasePath(r)}${r.source}`),
name: type,
fn: async (_req, _res, _params, _parsedUrl) => ({ finished: false }),
} as Route & Rewrite & Header)
Expand All @@ -458,7 +463,13 @@ export default class Server {
if (!value.includes(':')) {
return value
}
const { parsedDestination } = prepareDestination(value, params, {})
const { parsedDestination } = prepareDestination(
value,
params,
{},
false,
''
)

if (
!parsedDestination.pathname ||
Expand Down Expand Up @@ -507,7 +518,9 @@ export default class Server {
const { parsedDestination } = prepareDestination(
redirectRoute.destination,
params,
parsedUrl.query
parsedUrl.query,
false,
getCustomRouteBasePath(redirectRoute)
)
const updatedDestination = formatUrl(parsedDestination)

Expand All @@ -531,6 +544,7 @@ export default class Server {
const rewrites = this.customRoutes.rewrites.map((rewrite) => {
const rewriteRoute = getCustomRoute(rewrite, 'rewrite')
return {
...rewriteRoute,
check: true,
type: rewriteRoute.type,
name: `Rewrite route`,
Expand All @@ -540,7 +554,8 @@ export default class Server {
rewriteRoute.destination,
params,
parsedUrl.query,
true
true,
getCustomRouteBasePath(rewriteRoute)
)

// external rewrite, proxy it
Expand All @@ -560,8 +575,9 @@ export default class Server {
finished: true,
}
}
;(req as any)._nextDidRewrite = true
;(req as any)._nextRewroteUrl = newUrl
;(req as any)._nextDidRewrite =
(req as any)._nextRewroteUrl !== req.url

return {
finished: false,
Expand Down Expand Up @@ -618,6 +634,7 @@ export default class Server {
catchAllRoute,
useFileSystemPublicRoutes,
dynamicRoutes: this.dynamicRoutes,
basePath: this.nextConfig.basePath,
pageChecker: this.hasPage.bind(this),
}
}
Expand Down
3 changes: 1 addition & 2 deletions packages/next/next-server/server/render.tsx
Expand Up @@ -304,8 +304,7 @@ export async function renderToHTML(

const headTags = (...args: any) => callMiddleware('headTags', args)

const didRewrite =
(req as any)._nextDidRewrite && (req as any)._nextRewroteUrl !== req.url
const didRewrite = (req as any)._nextDidRewrite
const isFallback = !!query.__nextFallback
delete query.__nextFallback

Expand Down