Skip to content

fix: revert publish from subdirectory #1771

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

Merged
merged 1 commit into from
Nov 14, 2022
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 23 additions & 19 deletions packages/runtime/src/helpers/files.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable max-lines */
import { cpus } from 'os'

import type { NetlifyConfig } from '@netlify/build'
import { yellowBright } from 'chalk'
import { existsSync, readJson, move, copy, writeJson, readFile, writeFile, ensureDir, readFileSync } from 'fs-extra'
import globby from 'globby'
Expand Down Expand Up @@ -59,11 +60,11 @@ export const matchesRewrite = (file: string, rewrites: Rewrites): boolean => {
return matchesRedirect(file, rewrites.beforeFiles)
}

export const getMiddleware = async (distDir: string): Promise<Array<string>> => {
export const getMiddleware = async (publish: string): Promise<Array<string>> => {
if (process.env.NEXT_DISABLE_NETLIFY_EDGE !== 'true' && process.env.NEXT_DISABLE_NETLIFY_EDGE !== '1') {
return []
}
const manifestPath = join(distDir, 'server', 'middleware-manifest.json')
const manifestPath = join(publish, 'server', 'middleware-manifest.json')
if (existsSync(manifestPath)) {
const manifest = await readJson(manifestPath, { throws: false })
return manifest?.sortedMiddleware ?? []
Expand All @@ -73,28 +74,32 @@ export const getMiddleware = async (distDir: string): Promise<Array<string>> =>

// eslint-disable-next-line max-lines-per-function
export const moveStaticPages = async ({
distDir,
netlifyConfig,
target,
i18n,
basePath,
publishDir,
}: {
distDir: string
netlifyConfig: NetlifyConfig
target: 'server' | 'serverless' | 'experimental-serverless-trace'
i18n: NextConfig['i18n']
basePath?: string
publishDir
}): Promise<void> => {
console.log('Moving static page files to serve from CDN...')
const outputDir = join(distDir, 'server')
const outputDir = join(netlifyConfig.build.publish, target === 'server' ? 'server' : 'serverless')
const root = join(outputDir, 'pages')
const buildId = readFileSync(join(distDir, 'BUILD_ID'), 'utf8').trim()
const buildId = readFileSync(join(netlifyConfig.build.publish, 'BUILD_ID'), 'utf8').trim()
const dataDir = join('_next', 'data', buildId)
await ensureDir(join(publishDir, dataDir))
await ensureDir(join(netlifyConfig.build.publish, dataDir))
// Load the middleware manifest so we can check if a file matches it before moving
const middlewarePaths = await getMiddleware(distDir)
const middlewarePaths = await getMiddleware(netlifyConfig.build.publish)
const middleware = middlewarePaths.map((path) => path.slice(1))

const prerenderManifest: PrerenderManifest = await readJson(join(distDir, 'prerender-manifest.json'))
const { redirects, rewrites }: RoutesManifest = await readJson(join(distDir, 'routes-manifest.json'))
const prerenderManifest: PrerenderManifest = await readJson(
join(netlifyConfig.build.publish, 'prerender-manifest.json'),
)
const { redirects, rewrites }: RoutesManifest = await readJson(
join(netlifyConfig.build.publish, 'routes-manifest.json'),
)

const isrFiles = new Set<string>()

Expand Down Expand Up @@ -123,7 +128,7 @@ export const moveStaticPages = async ({
files.push(file)
filesManifest[file] = targetPath

const dest = join(publishDir, targetPath)
const dest = join(netlifyConfig.build.publish, targetPath)

try {
await move(source, dest)
Expand Down Expand Up @@ -237,10 +242,10 @@ export const moveStaticPages = async ({
}

// Write the manifest for use in the serverless functions
await writeJson(join(distDir, 'static-manifest.json'), Object.entries(filesManifest))
await writeJson(join(netlifyConfig.build.publish, 'static-manifest.json'), Object.entries(filesManifest))

if (i18n?.defaultLocale) {
const rootPath = basePath ? join(publishDir, basePath) : publishDir
const rootPath = basePath ? join(netlifyConfig.build.publish, basePath) : netlifyConfig.build.publish
// Copy the default locale into the root
const defaultLocaleDir = join(rootPath, i18n.defaultLocale)
if (existsSync(defaultLocaleDir)) {
Expand Down Expand Up @@ -422,13 +427,12 @@ export const unpatchNextFiles = async (root: string): Promise<void> => {
export const movePublicFiles = async ({
appDir,
outdir,
publishDir,
publish,
}: {
appDir: string
outdir?: string
publishDir: string
publish: string
}): Promise<void> => {
await ensureDir(publishDir)
// `outdir` is a config property added when using Next.js with Nx. It's typically
// a relative path outside of the appDir, e.g. '../../dist/apps/<app-name>', and
// the parent directory of the .next directory.
Expand All @@ -437,7 +441,7 @@ export const movePublicFiles = async ({
// directory from the original app directory.
const publicDir = outdir ? join(appDir, outdir, 'public') : join(appDir, 'public')
if (existsSync(publicDir)) {
await copy(publicDir, `${publishDir}/`)
await copy(publicDir, `${publish}/`)
}
}
/* eslint-enable max-lines */
27 changes: 26 additions & 1 deletion packages/runtime/src/helpers/redirects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { PrerenderManifest, SsgRoute } from 'next/dist/build'
import { outdent } from 'outdent'
import { join } from 'pathe'

import { HANDLER_FUNCTION_PATH, ODB_FUNCTION_PATH } from '../constants'
import { HANDLER_FUNCTION_PATH, HIDDEN_PATHS, ODB_FUNCTION_PATH } from '../constants'

import { getMiddleware } from './files'
import { ApiRouteConfig } from './functions'
Expand All @@ -25,6 +25,14 @@ import {
const matchesMiddleware = (middleware: Array<string>, route: string): boolean =>
middleware.some((middlewarePath) => route.startsWith(middlewarePath))

const generateHiddenPathRedirects = ({ basePath }: Pick<NextConfig, 'basePath'>): NetlifyConfig['redirects'] =>
HIDDEN_PATHS.map((path) => ({
from: `${basePath}${path}`,
to: '/404.html',
status: 404,
force: true,
}))

const generateLocaleRedirects = ({
i18n,
basePath,
Expand Down Expand Up @@ -58,6 +66,21 @@ const generateLocaleRedirects = ({
return redirects
}

export const generateStaticRedirects = ({
netlifyConfig,
nextConfig: { i18n, basePath },
}: {
netlifyConfig: NetlifyConfig
nextConfig: Pick<NextConfig, 'i18n' | 'basePath'>
}) => {
// Static files are in `static`
netlifyConfig.redirects.push({ from: `${basePath}/_next/static/*`, to: `/static/:splat`, status: 200 })

if (i18n) {
netlifyConfig.redirects.push({ from: `${basePath}/:locale/_next/static/*`, to: `/static/:splat`, status: 200 })
}
}

/**
* Routes that match middleware need to always use the SSR function
* This generates a rewrite for every middleware in every locale, both with and without a splat
Expand Down Expand Up @@ -220,6 +243,8 @@ export const generateRedirects = async ({
join(netlifyConfig.build.publish, 'routes-manifest.json'),
)

netlifyConfig.redirects.push(...generateHiddenPathRedirects({ basePath }))

if (i18n && i18n.localeDetection !== false) {
netlifyConfig.redirects.push(...generateLocaleRedirects({ i18n, basePath, trailingSlash }))
}
Expand Down
56 changes: 28 additions & 28 deletions packages/runtime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { join, relative } from 'path'
import type { NetlifyPlugin } from '@netlify/build'
import { bold, redBright } from 'chalk'
import destr from 'destr'
import { copy, ensureDir, existsSync, readFileSync } from 'fs-extra'
import { existsSync, readFileSync } from 'fs-extra'
import { outdent } from 'outdent'

import { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME } from './constants'
Expand All @@ -26,7 +26,7 @@ import {
getExtendedApiRouteConfigs,
warnOnApiRoutes,
} from './helpers/functions'
import { generateRedirects } from './helpers/redirects'
import { generateRedirects, generateStaticRedirects } from './helpers/redirects'
import { shouldSkip, isNextAuthInstalled, getCustomImageResponseHeaders, getRemotePatterns } from './helpers/utils'
import {
verifyNetlifyBuildVersion,
Expand Down Expand Up @@ -80,18 +80,12 @@ const plugin: NetlifyPlugin = {

checkNextSiteHasBuilt({ publish, failBuild })

const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir, experimental, distDir } =
await getNextConfig({
const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir, experimental } = await getNextConfig(
{
publish,
failBuild,
})

const dotNextDir = join(appDir, distDir)

// This is the *generated* publish dir. The user specifies .next, be we actually use this subdirectory
const publishDir = join(dotNextDir, 'dist')
await ensureDir(publishDir)

},
)
await cleanupEdgeFunctions(constants)

const middlewareManifest = await loadMiddlewareManifest(netlifyConfig)
Expand Down Expand Up @@ -123,7 +117,7 @@ const plugin: NetlifyPlugin = {
}

if (isNextAuthInstalled()) {
const config = await getRequiredServerFiles(dotNextDir)
const config = await getRequiredServerFiles(publish)

const userDefinedNextAuthUrl = config.config.env.NEXTAUTH_URL

Expand All @@ -140,7 +134,7 @@ const plugin: NetlifyPlugin = {
)
config.config.env.NEXTAUTH_URL = nextAuthUrl

await updateRequiredServerFiles(dotNextDir, config)
await updateRequiredServerFiles(publish, config)
} else {
// Using the deploy prime url in production leads to issues because the unique deploy ID is part of the generated URL
// and will not match the expected URL in the callback URL of an OAuth application.
Expand All @@ -151,27 +145,30 @@ const plugin: NetlifyPlugin = {
console.log(`NextAuth package detected, setting NEXTAUTH_URL environment variable to ${nextAuthUrl}`)
config.config.env.NEXTAUTH_URL = nextAuthUrl

await updateRequiredServerFiles(dotNextDir, config)
await updateRequiredServerFiles(publish, config)
}
}

const buildId = readFileSync(join(dotNextDir, 'BUILD_ID'), 'utf8').trim()
const buildId = readFileSync(join(publish, 'BUILD_ID'), 'utf8').trim()

await configureHandlerFunctions({ netlifyConfig, ignore, publish: relative(process.cwd(), dotNextDir) })
const apiRoutes = await getExtendedApiRouteConfigs(dotNextDir, appDir)
await configureHandlerFunctions({ netlifyConfig, ignore, publish: relative(process.cwd(), publish) })
const apiRoutes = await getExtendedApiRouteConfigs(publish, appDir)

await generateFunctions(constants, appDir, apiRoutes)
await generatePagesResolver({ target, constants })

await movePublicFiles({ appDir, outdir, publishDir })
await movePublicFiles({ appDir, outdir, publish })

await patchNextFiles(appDir)

if (!destr(process.env.SERVE_STATIC_FILES_FROM_ORIGIN)) {
await moveStaticPages({ distDir: dotNextDir, i18n, basePath, publishDir })
await moveStaticPages({ target, netlifyConfig, i18n, basePath })
}

await copy(join(dotNextDir, 'static'), join(publishDir, '_next', 'static'))
await generateStaticRedirects({
netlifyConfig,
nextConfig: { basePath, i18n },
})

await setupImageFunction({
constants,
Expand All @@ -193,16 +190,20 @@ const plugin: NetlifyPlugin = {
},

async onPostBuild({
netlifyConfig,
netlifyConfig: {
build: { publish },
redirects,
headers,
},
utils: {
status,
cache,
functions,
build: { failBuild },
},
constants: { FUNCTIONS_DIST, PUBLISH_DIR },
constants: { FUNCTIONS_DIST },
}) {
await saveCache({ cache, publish: netlifyConfig.build.publish })
await saveCache({ cache, publish })

if (shouldSkip()) {
status.show({
Expand All @@ -218,16 +219,15 @@ const plugin: NetlifyPlugin = {

await checkForOldFunctions({ functions })
await checkZipSize(join(FUNCTIONS_DIST, `${ODB_FUNCTION_NAME}.zip`))
const nextConfig = await getNextConfig({ publish: netlifyConfig.build.publish, failBuild })
const nextConfig = await getNextConfig({ publish, failBuild })

const { basePath, appDir } = nextConfig

generateCustomHeaders(nextConfig, netlifyConfig.headers)
generateCustomHeaders(nextConfig, headers)

warnForProblematicUserRewrites({ basePath, redirects: netlifyConfig.redirects })
warnForProblematicUserRewrites({ basePath, redirects })
warnForRootRedirects({ appDir })
await warnOnApiRoutes({ FUNCTIONS_DIST })
netlifyConfig.build.publish = join(PUBLISH_DIR, 'dist')
},
}
// The types haven't been updated yet
Expand Down
Loading