next.js/test/e2e/deferred-entries/next.config.js
next.config.js107 lines3.4 KB
const fs = require('fs')
const path = require('path')

const logFile = path.join(__dirname, '.entry-log')
const callbackLogFile = path.join(__dirname, '.callback-log')
const deferredPageFile = path.join(__dirname, 'app', 'deferred', 'page.tsx')
const routeHandlerFile = path.join(
  __dirname,
  'app',
  'route-handler',
  'route.ts'
)
const homePageFile = path.join(__dirname, 'app', 'page.tsx')

let lastHomePageContent = null

/** @type {import('next').NextConfig} */
module.exports = {
  experimental: {
    deferredEntries: ['/deferred', '/route-handler'],
    onBeforeDeferredEntries: async () => {
      const timestamp = Date.now()
      const homePageContent = fs.readFileSync(homePageFile, 'utf-8')
      const shouldWriteDeferredTimestamp =
        lastHomePageContent === null || lastHomePageContent !== homePageContent

      // Mutate the deferred entry source directly so the deferred build picks
      // up callback-time content.
      if (shouldWriteDeferredTimestamp) {
        const deferredPageContent = fs.readFileSync(deferredPageFile, 'utf-8')
        const nextDeferredPageContent = deferredPageContent.replace(
          /const CALLBACK_TIMESTAMP = \d+/,
          `const CALLBACK_TIMESTAMP = ${timestamp}`
        )
        if (nextDeferredPageContent === deferredPageContent) {
          throw new Error(
            'Failed to update CALLBACK_TIMESTAMP in deferred page entry'
          )
        }
        fs.writeFileSync(deferredPageFile, nextDeferredPageContent)

        const routeHandlerContent = fs.readFileSync(routeHandlerFile, 'utf-8')
        const nextRouteHandlerContent = routeHandlerContent.replace(
          /const ROUTE_HANDLER_CALLBACK_TIMESTAMP = \d+/,
          `const ROUTE_HANDLER_CALLBACK_TIMESTAMP = ${timestamp}`
        )
        if (nextRouteHandlerContent === routeHandlerContent) {
          throw new Error(
            'Failed to update ROUTE_HANDLER_CALLBACK_TIMESTAMP in route handler entry'
          )
        }
        fs.writeFileSync(routeHandlerFile, nextRouteHandlerContent)

        // Persist only write-triggering callback timestamps.
        // This avoids deferred self-rebuild callback loops in webpack dev.
        fs.writeFileSync(callbackLogFile, `callback:${timestamp}\n`)
      }

      lastHomePageContent = homePageContent

      console.log(
        `[TEST] onBeforeDeferredEntries callback executed at ${timestamp} (write=${shouldWriteDeferredTimestamp})`
      )

      // Small delay to ensure we can verify timing
      await new Promise((resolve) => setTimeout(resolve, 100))

      // Append to entry log to mark callback position in the build sequence
      fs.appendFileSync(logFile, `${timestamp}:CALLBACK_EXECUTED\n`)
    },
  },
  // Turbopack loader configuration
  turbopack: {
    rules: {
      '*ts': {
        loaders: [
          {
            loader: path.join(__dirname, 'entry-logger-loader.js'),
          },
        ],
      },
      '*.tsx': {
        loaders: [
          {
            loader: path.join(__dirname, 'entry-logger-loader.js'),
          },
        ],
      },
    },
  },
  // Webpack loader configuration
  webpack: (config, { isServer }) => {
    // Add the entry logger loader to track when entries are processed
    config.module.rules.push({
      test: /\.(tsx|ts|js|jsx)$/,
      include: [path.join(__dirname, 'app'), path.join(__dirname, 'pages')],
      use: [
        {
          loader: path.join(__dirname, 'entry-logger-loader.js'),
        },
      ],
    })

    return config
  },
}
Quest for Codev2.0.0
/
SIGN IN