Skip to content

Commit

Permalink
Track page counts during builds (#42766)
Browse files Browse the repository at this point in the history
Ensures we keep track of page counts during builds. 

## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have a helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the
feature request has been accepted for implementation before opening a
PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have a helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `pnpm build && pnpm lint`
- [ ] The "examples guidelines" are followed from [our contributing
doc](https://github.com/vercel/next.js/blob/canary/contributing/examples/adding-examples.md)
  • Loading branch information
ijjk committed Nov 11, 2022
1 parent 028b8d0 commit 3a22e97
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 21 deletions.
44 changes: 38 additions & 6 deletions packages/next/build/index.ts
Expand Up @@ -597,6 +597,7 @@ export default async function build(
appPageKeys.push(normalizedAppPageKey)
}
}
const totalAppPagesCount = appPageKeys.length

const pageKeys = {
pages: pagesPageKeys,
Expand Down Expand Up @@ -1097,6 +1098,7 @@ export default async function build(
telemetry.record(
eventBuildCompleted(pagesPaths, {
durationInSeconds: webpackBuildEnd[0],
totalAppPagesCount,
})
)

Expand All @@ -1121,6 +1123,10 @@ export default async function build(
const buildManifestPath = path.join(distDir, BUILD_MANIFEST)
const appBuildManifestPath = path.join(distDir, APP_BUILD_MANIFEST)

let staticAppPagesCount = 0
let serverAppPagesCount = 0
let edgeRuntimeAppCount = 0
let edgeRuntimePagesCount = 0
const ssgPages = new Set<string>()
const ssgStaticFallbackPages = new Set<string>()
const ssgBlockingFallbackPages = new Set<string>()
Expand Down Expand Up @@ -1300,6 +1306,18 @@ export default async function build(
config.experimental.gzipSize
)

const middlewareManifest: MiddlewareManifest = require(join(
distDir,
SERVER_DIRECTORY,
MIDDLEWARE_MANIFEST
))

for (const key of Object.keys(middlewareManifest?.functions)) {
if (key.startsWith('/api')) {
edgeRuntimePagesCount++
}
}

await Promise.all(
Object.entries(pageKeys)
.reduce<Array<{ pageType: keyof typeof pageKeys; page: string }>>(
Expand Down Expand Up @@ -1388,15 +1406,16 @@ export default async function build(
let edgeInfo: any

if (pageRuntime === SERVER_RUNTIME.edge) {
const manifest = require(join(
distDir,
SERVER_DIRECTORY,
MIDDLEWARE_MANIFEST
))
if (pageType === 'app') {
edgeRuntimeAppCount++
} else {
edgeRuntimePagesCount++
}

const manifestKey =
pageType === 'pages' ? page : originalAppPath || ''

edgeInfo = manifest.functions[manifestKey]
edgeInfo = middlewareManifest.functions[manifestKey]
}

let isPageStaticSpan =
Expand Down Expand Up @@ -1581,6 +1600,14 @@ export default async function build(
}
}

if (pageType === 'app') {
if (isSsg || isStatic) {
staticAppPagesCount++
} else {
serverAppPagesCount++
}
}

pageInfos.set(page, {
size: selfSize,
totalSize: allSize,
Expand Down Expand Up @@ -2589,6 +2616,11 @@ export default async function build(
.length,
redirectsWithHasCount: redirects.filter((r: any) => !!r.has).length,
middlewareCount: Object.keys(rootPaths).length > 0 ? 1 : 0,
totalAppPagesCount,
staticAppPagesCount,
serverAppPagesCount,
edgeRuntimeAppCount,
edgeRuntimePagesCount,
})
)

Expand Down
19 changes: 12 additions & 7 deletions packages/next/build/webpack/plugins/middleware-plugin.ts
Expand Up @@ -683,6 +683,7 @@ function getExtractMetadata(params: {
wasmBindings: new Map(),
assetBindings: new Map(),
}
let ogImageGenerationCount = 0

for (const module of modules) {
const buildInfo = getModuleBuildInfo(module)
Expand All @@ -697,13 +698,10 @@ function getExtractMetadata(params: {
/[\\/]node_modules[\\/]@vercel[\\/]og[\\/]dist[\\/]index.js$/.test(
resource
)
telemetry.record({
eventName: EVENT_BUILD_FEATURE_USAGE,
payload: {
featureName: 'vercelImageGeneration',
invocationCount: hasOGImageGeneration ? 1 : 0,
},
})

if (hasOGImageGeneration) {
ogImageGenerationCount++
}
}

/**
Expand Down Expand Up @@ -818,6 +816,13 @@ function getExtractMetadata(params: {
}
}

telemetry.record({
eventName: EVENT_BUILD_FEATURE_USAGE,
payload: {
featureName: 'vercelImageGeneration',
invocationCount: ogImageGenerationCount,
},
})
metadataByEntry.set(entryName, entryMetadata)
}
}
Expand Down
12 changes: 12 additions & 0 deletions packages/next/telemetry/events/build.ts
Expand Up @@ -56,6 +56,7 @@ type EventBuildCompleted = {
totalPageCount: number
hasDunderPages: boolean
hasTestPages: boolean
totalAppPagesCount?: number
}

export function eventBuildCompleted(
Expand All @@ -77,6 +78,7 @@ export function eventBuildCompleted(
(path) =>
REGEXP_DIRECTORY_TESTS.test(path) || REGEXP_FILE_TEST.test(path)
),
totalAppPagesCount: event.totalAppPagesCount,
},
}
}
Expand All @@ -100,6 +102,11 @@ type EventBuildOptimized = {
rewritesWithHasCount: number
redirectsWithHasCount: number
middlewareCount: number
totalAppPagesCount?: number
staticAppPagesCount?: number
serverAppPagesCount?: number
edgeRuntimeAppCount?: number
edgeRuntimePagesCount?: number
}

export function eventBuildOptimize(
Expand All @@ -121,6 +128,11 @@ export function eventBuildOptimize(
(path) =>
REGEXP_DIRECTORY_TESTS.test(path) || REGEXP_FILE_TEST.test(path)
),
totalAppPagesCount: event.totalAppPagesCount,
staticAppPagesCount: event.staticAppPagesCount,
serverAppPagesCount: event.serverAppPagesCount,
edgeRuntimeAppCount: event.edgeRuntimeAppCount,
edgeRuntimePagesCount: event.edgeRuntimePagesCount,
},
}
}
Expand Down
23 changes: 23 additions & 0 deletions test/integration/telemetry/pages/edge.js
@@ -0,0 +1,23 @@
import Image from 'next/image'
import LegacyImage from 'next/legacy/image'
import profilePic from '../public/small.jpg'

export const config = {
runtime: 'experimental-edge',
}

function About() {
return (
<>
<h1>My Homepage</h1>
<Image src={profilePic} alt="Picture of the author" />
<p>Welcome to my homepage!</p>
</>
)
}

export default About

export function AboutFutureImage() {
return <LegacyImage src={profilePic} alt="Picture of the author" />
}
107 changes: 99 additions & 8 deletions test/integration/telemetry/test/index.test.js
Expand Up @@ -331,9 +331,14 @@ describe('Telemetry CLI', () => {
const event1 = /NEXT_BUILD_OPTIMIZED[\s\S]+?{([\s\S]+?)}/.exec(stderr).pop()
expect(event1).toMatch(/"staticPropsPageCount": 2/)
expect(event1).toMatch(/"serverPropsPageCount": 2/)
expect(event1).toMatch(/"ssrPageCount": 2/)
expect(event1).toMatch(/"ssrPageCount": 3/)
expect(event1).toMatch(/"staticPageCount": 4/)
expect(event1).toMatch(/"totalPageCount": 10/)
expect(event1).toMatch(/"totalPageCount": 11/)
expect(event1).toMatch(/"totalAppPagesCount": 0/)
expect(event1).toMatch(/"staticAppPagesCount": 0/)
expect(event1).toMatch(/"serverAppPagesCount": 0/)
expect(event1).toMatch(/"edgeRuntimeAppCount": 0/)
expect(event1).toMatch(/"edgeRuntimePagesCount": 2/)
})

it('detects isSrcDir dir correctly for `next dev`', async () => {
Expand Down Expand Up @@ -377,7 +382,19 @@ describe('Telemetry CLI', () => {
)
await fs.mkdir(path.join(__dirname, '../app'))
await fs.writeFile(
path.join(__dirname, '../app/page.js'),
path.join(__dirname, '../app/layout.js'),
`
export default function RootLayout({ children }) {
return <html>
<head/>
<body>{children}</body>
</html>
}
`
)
await fs.ensureFile(path.join(__dirname, '../app/hello/page.js'))
await fs.writeFile(
path.join(__dirname, '../app/hello/page.js'),
'export default function Page() { return "hello world" }'
)

Expand Down Expand Up @@ -490,6 +507,73 @@ describe('Telemetry CLI', () => {
}
})

it('should detect app page counts', async () => {
const teardown = await setupAppDir()

try {
await fs.ensureFile(path.join(__dirname, '../app/ssr/page.js'))
await fs.writeFile(
path.join(__dirname, '../app/ssr/page.js'),
`
export const revalidate = 0
export default function Page() {
return <p>ssr page</p>
}
`
)
await fs.ensureFile(path.join(__dirname, '../app/edge-ssr/page.js'))
await fs.writeFile(
path.join(__dirname, '../app/edge-ssr/page.js'),
`
export const runtime = 'experimental-edge'
export default function Page() {
return <p>edge-ssr page</p>
}
`
)
await fs.ensureFile(path.join(__dirname, '../app/app-ssg/[slug]/page.js'))
await fs.writeFile(
path.join(__dirname, '../app/app-ssg/[slug]/page.js'),
`
export function generateStaticParams() {
return [
{ slug: 'post-1' },
{ slug: 'post-2' },
]
}
export default function Page() {
return <p>ssg page</p>
}
`
)
const { stderr } = await nextBuild(appDir, [], {
stderr: true,
env: { NEXT_TELEMETRY_DEBUG: 1 },
})

const event1 = /NEXT_BUILD_OPTIMIZED[\s\S]+?{([\s\S]+?)}/
.exec(stderr)
.pop()
expect(event1).toMatch(/"staticPropsPageCount": 2/)
expect(event1).toMatch(/"serverPropsPageCount": 2/)
expect(event1).toMatch(/"ssrPageCount": 3/)
expect(event1).toMatch(/"staticPageCount": 4/)
expect(event1).toMatch(/"totalPageCount": 11/)
expect(event1).toMatch(/"totalAppPagesCount": 4/)
expect(event1).toMatch(/"serverAppPagesCount": 2/)
expect(event1).toMatch(/"edgeRuntimeAppCount": 1/)
expect(event1).toMatch(/"edgeRuntimePagesCount": 2/)

const event2 = /NEXT_BUILD_COMPLETED[\s\S]+?{([\s\S]+?)}/
.exec(stderr)
.pop()

expect(event2).toMatch(/"totalAppPagesCount": 4/)
} finally {
await teardown()
}
})

it('detect reportWebVitals correctly for `next build`', async () => {
// Case 1: When _app.js does not exist.
let build = await nextBuild(appDir, [], {
Expand Down Expand Up @@ -750,6 +834,7 @@ describe('Telemetry CLI', () => {
stderr,
'NEXT_BUILD_FEATURE_USAGE'
)

expect(featureUsageEvents).toEqual(
expect.arrayContaining([
{
Expand All @@ -758,7 +843,7 @@ describe('Telemetry CLI', () => {
},
{
featureName: 'next/image',
invocationCount: 1,
invocationCount: 2,
},
{
featureName: 'next/script',
Expand Down Expand Up @@ -787,8 +872,14 @@ describe('Telemetry CLI', () => {
stderr: true,
env: { NEXT_TELEMETRY_DEBUG: 1 },
})
await fs.remove(path.join(appDir, 'next.config.js'))
await fs.remove(path.join(appDir, 'jsconfig.json'))
await fs.rename(
path.join(appDir, 'next.config.js'),
path.join(appDir, 'next.config.swc')
)
await fs.rename(
path.join(appDir, 'jsconfig.json'),
path.join(appDir, 'jsconfig.swc')
)
const featureUsageEvents = findAllTelemetryEvents(
stderr,
'NEXT_BUILD_FEATURE_USAGE'
Expand Down Expand Up @@ -963,11 +1054,11 @@ describe('Telemetry CLI', () => {
)
expect(featureUsageEvents).toContainEqual({
featureName: 'next/legacy/image',
invocationCount: 1,
invocationCount: 2,
})
expect(featureUsageEvents).toContainEqual({
featureName: 'next/image',
invocationCount: 1,
invocationCount: 2,
})
})

Expand Down

0 comments on commit 3a22e97

Please sign in to comment.