未验证 提交 55369051 编写于 作者: J Joe Haddad 提交者: GitHub

Warn build on duplicate pages (#8646)

* Fail build on duplicate pages
This will fail the `next build` command when a duplicate page is found.
In development, we'll emit a warning instead of crashing the dev server.

* Add test for warning in development

* Only issue a warning

* Fix production test

* Fix development test

* Remove useless arg

* Warn in development, too
上级 59485111
import { isTargetLikeServerless } from '../next-server/server/config'
import chalk from 'chalk'
import { join } from 'path'
import { stringify } from 'querystring'
import { API_ROUTE, DOT_NEXT_ALIAS, PAGES_DIR_ALIAS } from '../lib/constants'
import { isTargetLikeServerless } from '../next-server/server/config'
import { warn } from './output/log'
import { ServerlessLoaderQuery } from './webpack/loaders/next-serverless-loader'
type PagesMapping = {
......@@ -13,6 +15,7 @@ export function createPagesMapping(
pagePaths: string[],
extensions: string[]
): PagesMapping {
const previousPages: PagesMapping = {}
const pages: PagesMapping = pagePaths.reduce(
(result: PagesMapping, pagePath): PagesMapping => {
let page = `${pagePath
......@@ -20,10 +23,20 @@ export function createPagesMapping(
.replace(/\\/g, '/')}`.replace(/\/index$/, '')
page = page === '/index' ? '/' : page
result[page === '' ? '/' : page] = join(
PAGES_DIR_ALIAS,
pagePath
).replace(/\\/g, '/')
const pageKey = page === '' ? '/' : page
if (pageKey in result) {
warn(
`Duplicate page detected. ${chalk.cyan(
join('pages', previousPages[pageKey])
)} and ${chalk.cyan(
join('pages', pagePath)
)} both resolve to ${chalk.cyan(pageKey)}.`
)
} else {
previousPages[pageKey] = pagePath
}
result[pageKey] = join(PAGES_DIR_ALIAS, pagePath).replace(/\\/g, '/')
return result
},
{}
......
......@@ -94,11 +94,9 @@ export default async function build(dir: string, conf = null): Promise<void> {
// needed for static exporting since we want to replace with HTML
// files
const allPagePaths = [...pagePaths]
const allStaticPages = new Set<string>()
let allPageInfos = new Map<string, PageInfo>()
const allMappedPages = createPagesMapping(allPagePaths, config.pageExtensions)
const mappedPages = createPagesMapping(pagePaths, config.pageExtensions)
const entrypoints = createEntrypoints(mappedPages, target, buildId, config)
const configs = await Promise.all([
......@@ -202,7 +200,7 @@ export default async function build(dir: string, conf = null): Promise<void> {
console.log(chalk.green('Compiled successfully.\n'))
backgroundWork.push(
recordBuildDuration({
totalPageCount: allPagePaths.length,
totalPageCount: pagePaths.length,
durationInSeconds: webpackBuildEnd[0],
})
)
......@@ -412,9 +410,9 @@ export default async function build(dir: string, conf = null): Promise<void> {
backgroundWork.push(
recordBuildOptimize({
durationInSeconds: analysisEnd[0],
totalPageCount: allPagePaths.length,
totalPageCount: pagePaths.length,
staticPageCount: staticPages.size,
ssrPageCount: allPagePaths.length - staticPages.size,
ssrPageCount: pagePaths.length - staticPages.size,
})
)
......@@ -431,7 +429,7 @@ export default async function build(dir: string, conf = null): Promise<void> {
allPageInfos.set(key, info)
})
printTreeView(Object.keys(allMappedPages), allPageInfos, isLikeServerless)
printTreeView(Object.keys(mappedPages), allPageInfos, isLikeServerless)
if (tracer) {
const parsedResults = await tracer.profiler.stopProfiling()
......
import { join } from 'path'
import chalk from 'chalk'
import { isWriteable } from '../../build/is-writeable'
import { warn } from '../../build/output/log'
export async function findPageFile(
rootDir: string,
normalizedPagePath: string,
pageExtensions: string[]
): Promise<string | null> {
let foundPagePaths: string[] = []
for (const extension of pageExtensions) {
const relativePagePath = `${normalizedPagePath}.${extension}`
const pagePath = join(rootDir, relativePagePath)
if (await isWriteable(pagePath)) {
return relativePagePath
foundPagePaths.push(relativePagePath)
}
const relativePagePathWithIndex = join(
......@@ -20,9 +24,23 @@ export async function findPageFile(
)
const pagePathWithIndex = join(rootDir, relativePagePathWithIndex)
if (await isWriteable(pagePathWithIndex)) {
return relativePagePathWithIndex
foundPagePaths.push(relativePagePathWithIndex)
}
}
return null
if (foundPagePaths.length < 1) {
return null
}
if (foundPagePaths.length > 1) {
warn(
`Duplicate page detected. ${chalk.cyan(
join('pages', foundPagePaths[0])
)} and ${chalk.cyan(
join('pages', foundPagePaths[1])
)} both resolve to ${chalk.cyan(normalizedPagePath)}.`
)
}
return foundPagePaths[0]
}
export default () => (
<>
<h3>Hi 👋</h3>
</>
)
export default () => (
<>
<h3>Hi 👋... again</h3>
</>
)
/* eslint-env jest */
/* global jasmine */
import path from 'path'
import {
nextBuild,
findPort,
launchApp,
renderViaHTTP,
killApp,
waitFor
} from 'next-test-utils'
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 1
const appDir = path.join(__dirname, '..')
describe('Handles Duplicate Pages', () => {
describe('production', () => {
it('Throws an error during build', async () => {
const { stdout } = await nextBuild(appDir, [], { stdout: true })
expect(stdout).toContain('Duplicate page detected')
})
})
describe('dev mode', () => {
it('Shows warning in development', async () => {
let output
const handleOutput = msg => {
output += msg
}
const appPort = await findPort()
const app = await launchApp(appDir, appPort, {
onStdout: handleOutput,
onStderr: handleOutput
})
await renderViaHTTP(appPort, '/hello')
await waitFor(3000)
await killApp(app)
expect(output).toMatch(/Duplicate page detected/)
})
})
})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册