From 3a22e9768bad7797bd9bef47ba450cf52176f59f Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Thu, 10 Nov 2022 17:47:33 -0800 Subject: [PATCH] Track page counts during builds (#42766) 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) --- packages/next/build/index.ts | 44 ++++++- .../webpack/plugins/middleware-plugin.ts | 19 ++-- packages/next/telemetry/events/build.ts | 12 ++ test/integration/telemetry/pages/edge.js | 23 ++++ test/integration/telemetry/test/index.test.js | 107 ++++++++++++++++-- 5 files changed, 184 insertions(+), 21 deletions(-) create mode 100644 test/integration/telemetry/pages/edge.js diff --git a/packages/next/build/index.ts b/packages/next/build/index.ts index bae3d706a98f2..685c7356b5bb4 100644 --- a/packages/next/build/index.ts +++ b/packages/next/build/index.ts @@ -597,6 +597,7 @@ export default async function build( appPageKeys.push(normalizedAppPageKey) } } + const totalAppPagesCount = appPageKeys.length const pageKeys = { pages: pagesPageKeys, @@ -1097,6 +1098,7 @@ export default async function build( telemetry.record( eventBuildCompleted(pagesPaths, { durationInSeconds: webpackBuildEnd[0], + totalAppPagesCount, }) ) @@ -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() const ssgStaticFallbackPages = new Set() const ssgBlockingFallbackPages = new Set() @@ -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>( @@ -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 = @@ -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, @@ -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, }) ) diff --git a/packages/next/build/webpack/plugins/middleware-plugin.ts b/packages/next/build/webpack/plugins/middleware-plugin.ts index 08eac95755184..709eb8a07a2ca 100644 --- a/packages/next/build/webpack/plugins/middleware-plugin.ts +++ b/packages/next/build/webpack/plugins/middleware-plugin.ts @@ -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) @@ -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++ + } } /** @@ -818,6 +816,13 @@ function getExtractMetadata(params: { } } + telemetry.record({ + eventName: EVENT_BUILD_FEATURE_USAGE, + payload: { + featureName: 'vercelImageGeneration', + invocationCount: ogImageGenerationCount, + }, + }) metadataByEntry.set(entryName, entryMetadata) } } diff --git a/packages/next/telemetry/events/build.ts b/packages/next/telemetry/events/build.ts index 9ff23eae812ad..91e2dd0f92091 100644 --- a/packages/next/telemetry/events/build.ts +++ b/packages/next/telemetry/events/build.ts @@ -56,6 +56,7 @@ type EventBuildCompleted = { totalPageCount: number hasDunderPages: boolean hasTestPages: boolean + totalAppPagesCount?: number } export function eventBuildCompleted( @@ -77,6 +78,7 @@ export function eventBuildCompleted( (path) => REGEXP_DIRECTORY_TESTS.test(path) || REGEXP_FILE_TEST.test(path) ), + totalAppPagesCount: event.totalAppPagesCount, }, } } @@ -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( @@ -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, }, } } diff --git a/test/integration/telemetry/pages/edge.js b/test/integration/telemetry/pages/edge.js new file mode 100644 index 0000000000000..6d0a32122c75e --- /dev/null +++ b/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 ( + <> +

My Homepage

+ Picture of the author +

Welcome to my homepage!

+ + ) +} + +export default About + +export function AboutFutureImage() { + return +} diff --git a/test/integration/telemetry/test/index.test.js b/test/integration/telemetry/test/index.test.js index 6c65320887942..3558f6360ab49 100644 --- a/test/integration/telemetry/test/index.test.js +++ b/test/integration/telemetry/test/index.test.js @@ -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 () => { @@ -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 + + {children} + + } + ` + ) + 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" }' ) @@ -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

ssr page

+ } + ` + ) + 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

edge-ssr page

+ } + ` + ) + 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

ssg page

+ } + ` + ) + 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, [], { @@ -750,6 +834,7 @@ describe('Telemetry CLI', () => { stderr, 'NEXT_BUILD_FEATURE_USAGE' ) + expect(featureUsageEvents).toEqual( expect.arrayContaining([ { @@ -758,7 +843,7 @@ describe('Telemetry CLI', () => { }, { featureName: 'next/image', - invocationCount: 1, + invocationCount: 2, }, { featureName: 'next/script', @@ -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' @@ -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, }) })