Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions packages/next/src/build/build-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export const NextBuildContext: Partial<{
mappedRootPaths: {
[page: string]: string
}
hasInstrumentationHook: boolean

// misc fields
telemetryPlugin: TelemetryPlugin
Expand Down
18 changes: 18 additions & 0 deletions packages/next/src/build/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ROOT_DIR_ALIAS,
APP_DIR_ALIAS,
WEBPACK_LAYERS,
INSTRUMENTATION_HOOK_FILENAME,
} from '../lib/constants'
import { isAPIRoute } from '../lib/is-api-route'
import { isEdgeRuntime } from '../lib/is-edge-runtime'
Expand All @@ -36,6 +37,7 @@ import { warn } from './output/log'
import {
isMiddlewareFile,
isMiddlewareFilename,
isInstrumentationHookFile,
NestedMiddlewareError,
} from './utils'
import { getPageStaticInfo } from './analysis/get-page-static-info'
Expand Down Expand Up @@ -148,6 +150,7 @@ export interface CreateEntrypointsParams {
appDir?: string
appPaths?: Record<string, string>
pageExtensions: string[]
hasInstrumentationHook?: boolean
}

export function getEdgeServerEntry(opts: {
Expand All @@ -163,6 +166,7 @@ export function getEdgeServerEntry(opts: {
middleware?: Partial<MiddlewareConfig>
pagesType: 'app' | 'pages' | 'root'
appDirLoader?: string
hasInstrumentationHook?: boolean
}) {
if (
opts.pagesType === 'app' &&
Expand Down Expand Up @@ -200,6 +204,13 @@ export function getEdgeServerEntry(opts: {
return `next-edge-function-loader?${stringify(loaderParams)}!`
}

if (isInstrumentationHookFile(opts.page)) {
return {
import: opts.page,
filename: `edge-${INSTRUMENTATION_HOOK_FILENAME}.js`,
}
}

const loaderParams: EdgeSSRLoaderQuery = {
absolute500Path: opts.pages['/500'] || '',
absoluteAppPath: opts.pages['/_app'],
Expand Down Expand Up @@ -267,7 +278,13 @@ export async function runDependingOnPageType<T>(params: {
onServer: () => T
page: string
pageRuntime: ServerRuntime
pagesType?: 'app' | 'pages' | 'root'
}): Promise<void> {
if (params.pagesType === 'root' && isInstrumentationHookFile(params.page)) {
await Promise.all([params.onServer(), params.onEdgeServer()])
return
}

if (isMiddlewareFile(params.page)) {
await params.onEdgeServer()
return
Expand Down Expand Up @@ -404,6 +421,7 @@ export async function createEntrypoints(params: CreateEntrypointsParams) {
await runDependingOnPageType({
page,
pageRuntime: staticInfo.runtime,
pagesType,
onClient: () => {
if (isServerComponent || isInsideAppDir) {
// We skip the initial entries for server component pages and let the
Expand Down
17 changes: 16 additions & 1 deletion packages/next/src/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
PUBLIC_DIR_MIDDLEWARE_CONFLICT,
MIDDLEWARE_FILENAME,
PAGES_DIR_ALIAS,
INSTRUMENTATION_HOOK_FILENAME,
} from '../lib/constants'
import { fileExists } from '../lib/file-exists'
import { findPagesDir } from '../lib/find-pages-dir'
Expand Down Expand Up @@ -513,11 +514,25 @@ export default async function build(
`^${MIDDLEWARE_FILENAME}\\.(?:${config.pageExtensions.join('|')})$`
)

const instrumentationHookDetectionRegExp = new RegExp(
`^${INSTRUMENTATION_HOOK_FILENAME}\\.(?:${config.pageExtensions.join(
'|'
)})$`
)

const rootDir = path.join((pagesDir || appDir)!, '..')
const rootPaths = (
await flatReaddir(rootDir, middlewareDetectionRegExp)
await flatReaddir(rootDir, [
middlewareDetectionRegExp,
instrumentationHookDetectionRegExp,
])
).map((absoluteFile) => absoluteFile.replace(dir, ''))

const hasInstrumentationHook = rootPaths.some((p) =>
p.includes(INSTRUMENTATION_HOOK_FILENAME)
)
NextBuildContext.hasInstrumentationHook = hasInstrumentationHook

// needed for static exporting since we want to replace with HTML
// files

Expand Down
24 changes: 24 additions & 0 deletions packages/next/src/build/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
SERVER_PROPS_GET_INIT_PROPS_CONFLICT,
SERVER_PROPS_SSG_CONFLICT,
MIDDLEWARE_FILENAME,
INSTRUMENTATION_HOOK_FILENAME,
} from '../lib/constants'
import { MODERN_BROWSERSLIST_TARGET } from '../shared/lib/constants'
import prettyBytes from '../lib/pretty-bytes'
Expand Down Expand Up @@ -286,6 +287,13 @@ export function isMiddlewareFilename(file?: string) {
return file === MIDDLEWARE_FILENAME || file === `src/${MIDDLEWARE_FILENAME}`
}

export function isInstrumentationHookFilename(file?: string) {
return (
file === INSTRUMENTATION_HOOK_FILENAME ||
file === `src/${INSTRUMENTATION_HOOK_FILENAME}`
)
}

export interface PageInfo {
isHybridAmp?: boolean
size: number
Expand Down Expand Up @@ -1828,6 +1836,22 @@ export function isMiddlewareFile(file: string) {
)
}

export function isInstrumentationHookFile(file: string) {
return (
file === `/${INSTRUMENTATION_HOOK_FILENAME}` ||
file === `/src/${INSTRUMENTATION_HOOK_FILENAME}`
)
}

export function getPossibleInstrumentationHookFilenames(
folder: string,
extensions: string[]
) {
return extensions.map((extension) =>
path.join(folder, `${INSTRUMENTATION_HOOK_FILENAME}.${extension}`)
)
}

export function getPossibleMiddlewareFilenames(
folder: string,
extensions: string[]
Expand Down
11 changes: 7 additions & 4 deletions packages/next/src/build/webpack-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,37 +60,40 @@ async function webpackBuildImpl(): Promise<{
const nextBuildSpan = NextBuildContext.nextBuildSpan!
const buildSpinner = NextBuildContext.buildSpinner
const dir = NextBuildContext.dir!
const config = NextBuildContext.config!

const runWebpackSpan = nextBuildSpan.traceChild('run-webpack-compiler')
const entrypoints = await nextBuildSpan
.traceChild('create-entrypoints')
.traceAsyncFn(() =>
createEntrypoints({
buildId: NextBuildContext.buildId!,
config: NextBuildContext.config!,
config: config,
envFiles: NextBuildContext.loadedEnvFiles!,
isDev: false,
rootDir: dir,
pageExtensions: NextBuildContext.config!.pageExtensions!,
pageExtensions: config.pageExtensions!,
pagesDir: NextBuildContext.pagesDir!,
appDir: NextBuildContext.appDir!,
pages: NextBuildContext.mappedPages!,
appPaths: NextBuildContext.mappedAppPages!,
previewMode: NextBuildContext.previewProps!,
rootPaths: NextBuildContext.mappedRootPaths!,
hasInstrumentationHook: NextBuildContext.hasInstrumentationHook!,
})
)

const commonWebpackOptions = {
isServer: false,
buildId: NextBuildContext.buildId!,
config: NextBuildContext.config!,
target: NextBuildContext.config!.target!,
config: config,
target: config.target!,
appDir: NextBuildContext.appDir!,
pagesDir: NextBuildContext.pagesDir!,
rewrites: NextBuildContext.rewrites!,
reactProductionProfiling: NextBuildContext.reactProductionProfiling!,
noMangling: NextBuildContext.noMangling!,
hasInstrumentationHook: NextBuildContext.hasInstrumentationHook!,
}

const configs = await runWebpackSpan
Expand Down
3 changes: 3 additions & 0 deletions packages/next/src/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,7 @@ export default async function getBaseWebpackConfig(
jsConfig,
resolvedBaseUrl,
supportedBrowsers,
hasInstrumentationHook = false,
}: {
buildId: string
config: NextConfigComplete
Expand All @@ -636,6 +637,7 @@ export default async function getBaseWebpackConfig(
jsConfig: any
resolvedBaseUrl: string | undefined
supportedBrowsers: string[] | undefined
hasInstrumentationHook?: boolean
}
): Promise<webpack.Configuration> {
const isClient = compilerType === COMPILER_NAMES.client
Expand Down Expand Up @@ -2144,6 +2146,7 @@ export default async function getBaseWebpackConfig(
new MiddlewarePlugin({
dev,
sriEnabled: !dev && !!config.experimental.sri?.algorithm,
hasInstrumentationHook: hasInstrumentationHook,
}),
isClient &&
new BuildManifestPlugin({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export default async function edgeSSRLoader(this: any) {
import { getRender } from 'next/dist/esm/build/webpack/loaders/next-edge-ssr-loader/render'

enhanceGlobals()

const pageType = ${JSON.stringify(pagesType)}
${
isAppDir
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { NextConfig } from '../../../../server/config-shared'
import type { NextConfigComplete } from '../../../../server/config-shared'

import type { DocumentType, AppType } from '../../../../shared/lib/utils'
import type { BuildManifest } from '../../../../server/get-page-files'
Expand Down Expand Up @@ -48,7 +48,7 @@ export function getRender({
serverComponentManifest: any
serverCSSManifest: any
appServerMod: any
config: NextConfig
config: NextConfigComplete
buildId: string
fontLoaderManifest: FontLoaderManifest
}) {
Expand Down
31 changes: 25 additions & 6 deletions packages/next/src/build/webpack/plugins/middleware-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import {
NEXT_CLIENT_SSR_ENTRY_SUFFIX,
FLIGHT_SERVER_CSS_MANIFEST,
SUBRESOURCE_INTEGRITY_MANIFEST,
FONT_LOADER_MANIFEST,
} from '../../../shared/lib/constants'
import {
getPageStaticInfo,
Expand All @@ -29,6 +28,7 @@ import { Telemetry } from '../../../telemetry/storage'
import { traceGlobals } from '../../../trace/shared'
import { EVENT_BUILD_FEATURE_USAGE } from '../../../telemetry/events'
import { normalizeAppPath } from '../../../shared/lib/router/utils/app-paths'
import { INSTRUMENTATION_HOOK_FILENAME } from '../../../lib/constants'

export interface EdgeFunctionDefinition {
env: string[]
Expand Down Expand Up @@ -90,7 +90,10 @@ function isUsingIndirectEvalAndUsedByExports(args: {
function getEntryFiles(
entryFiles: string[],
meta: EntryMetadata,
opts: { sriEnabled: boolean }
opts: {
sriEnabled: boolean
hasInstrumentationHook: boolean
}
) {
const files: string[] = []
if (meta.edgeSSR) {
Expand Down Expand Up @@ -120,7 +123,9 @@ function getEntryFiles(
`server/${MIDDLEWARE_REACT_LOADABLE_MANIFEST}.js`
)

files.push(`server/${FONT_LOADER_MANIFEST}.js`)
if (opts.hasInstrumentationHook) {
files.push(`server/edge-${INSTRUMENTATION_HOOK_FILENAME}.js`)
}
}

files.push(
Expand All @@ -134,7 +139,10 @@ function getEntryFiles(
function getCreateAssets(params: {
compilation: webpack.Compilation
metadataByEntry: Map<string, EntryMetadata>
opts: { sriEnabled: boolean }
opts: {
sriEnabled: boolean
hasInstrumentationHook: boolean
}
}) {
const { compilation, metadataByEntry, opts } = params
return (assets: any) => {
Expand Down Expand Up @@ -785,10 +793,20 @@ function getExtractMetadata(params: {
export default class MiddlewarePlugin {
private readonly dev: boolean
private readonly sriEnabled: boolean

constructor({ dev, sriEnabled }: { dev: boolean; sriEnabled: boolean }) {
private readonly hasInstrumentationHook: boolean

constructor({
dev,
sriEnabled,
hasInstrumentationHook,
}: {
dev: boolean
sriEnabled: boolean
hasInstrumentationHook: boolean
}) {
this.dev = dev
this.sriEnabled = sriEnabled
this.hasInstrumentationHook = hasInstrumentationHook
}

public apply(compiler: webpack.Compiler) {
Expand Down Expand Up @@ -833,6 +851,7 @@ export default class MiddlewarePlugin {
metadataByEntry,
opts: {
sriEnabled: this.sriEnabled,
hasInstrumentationHook: this.hasInstrumentationHook,
},
})
)
Expand Down
37 changes: 34 additions & 3 deletions packages/next/src/cli/next-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { fileExists } from '../lib/file-exists'
import Watchpack from 'next/dist/compiled/watchpack'
import stripAnsi from 'next/dist/compiled/strip-ansi'
import { warn } from '../build/output/log'
import { getPossibleInstrumentationHookFilenames } from '../build/utils'

let isTurboSession = false
let sessionStopHandled = false
Expand Down Expand Up @@ -612,20 +613,50 @@ If you cannot make the changes above, but still want to try out\nNext.js v13 wit
const watchedEntryLength = parentDir.split('/').length + 1
const previousItems = new Set()

const files = getPossibleInstrumentationHookFilenames(
dir,
config.pageExtensions!
)

const wp = new Watchpack({
ignored: (entry: string) => {
// watch only one level
return !(entry.split('/').length <= watchedEntryLength)
return (
!(entry.split('/').length <= watchedEntryLength) &&
!files.includes(entry)
)
},
})

wp.watch({ directories: [parentDir], startTime: 0 })

let instrumentationFileTimeCache: number | undefined = undefined
wp.on('aggregated', () => {
const knownFiles = wp.getTimeInfoEntries()
const newFiles: string[] = []
let hasPagesApp = false

// check if the `instrumentation.js` has changed
// if it has we need to restart the server
const instrumentationFile = [...knownFiles.keys()].find((key) =>
files.includes(key)
)
if (instrumentationFile) {
const instrumentationFileTime =
knownFiles.get(instrumentationFile)?.timestamp
if (
instrumentationFileTimeCache !== undefined &&
instrumentationFileTime !== instrumentationFileTimeCache
) {
warn(
`The instrumentation file has changed, restarting the server to apply changes.`
)
childProcessExitUnsub()
childProcess?.kill()
childProcessExitUnsub = setupFork()
Copy link
Member

Choose a reason for hiding this comment

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

We don't do this for next.config.js - wondering if it's necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

there's no un-registering of the monkey patching usually done by tracing libraries so it's better to restart

} else {
instrumentationFileTimeCache = instrumentationFileTime
}
}

// if the dir still exists nothing to check
try {
const result = findPagesDir(dir, !!config.experimental?.appDir)
Expand Down
Loading