Skip to content

fix: prioritize src/ directory for instrumentation.ts in development #82513

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: canary
Choose a base branch
from

Conversation

devin-ai-integration[bot]
Copy link
Contributor

What?

Fixes inconsistent instrumentation.ts loading behavior between development and production environments when a src/ directory exists.

Why?

Problem:

  • Development: Loads instrumentation.ts from root directory even when src/ directory exists
  • Production: Correctly loads instrumentation.ts from src/ directory when it exists
  • Expected: Should exclusively load from src/ when src/ directory exists, ignoring any root-level file

This inconsistency causes confusion and potential issues when projects have both instrumentation.ts in root and src/instrumentation.ts, as different files are loaded in different environments.

How?

Modified getPossibleInstrumentationHookFilenames() in packages/next/src/build/utils.ts:

  1. Check for src/ directory existence using fs.existsSync()
  2. If src/ exists: Only return paths within src/ directory
  3. If no src/: Only return root directory paths
  4. Result: Development behavior now matches production behavior

Before:

// Always returned both root AND src paths
files.push(
  path.join(folder, `${INSTRUMENTATION_HOOK_FILENAME}.${extension}`),
  path.join(folder, `src`, `${INSTRUMENTATION_HOOK_FILENAME}.${extension}`)
)

After:

// Only returns src paths if src/ exists, otherwise only root paths
if (hasSrcDir) {
  files.push(path.join(folder, `src`, `${INSTRUMENTATION_HOOK_FILENAME}.${extension}`))
} else {
  files.push(path.join(folder, `${INSTRUMENTATION_HOOK_FILENAME}.${extension}`))
}

Testing

Created test script that validates:

  • ✅ When no src/ directory exists → only returns root paths
  • ✅ When src/ directory exists → only returns src paths

Human Review Checklist

Critical items to verify:

  • Logic correctly prioritizes src/ when directory exists
  • No regressions in development server instrumentation loading
  • File system check approach (fs.existsSync()) is appropriate for this context
  • Consider if existing tests need updates for this behavior change
  • Verify alignment with documented instrumentation.ts behavior

Potential risks:

  • Synchronous fs.existsSync() call in hot path (though just directory check)
  • Projects incorrectly depending on old buggy behavior might be affected
  • Runtime require('fs') usage (follows existing codebase pattern)

Link to Devin run: https://app.devin.ai/sessions/dd6bf37499764766864e431921c83280
Requested by: @devjiwonchoi

This change ensures consistent instrumentation.ts loading behavior across all Next.js environments.

When a src/ directory exists, the development server should exclusively
load instrumentation.ts from src/ and completely ignore any file in the
root directory. This aligns development behavior with production.

- Modified getPossibleInstrumentationHookFilenames() to check for src/ directory existence
- If src/ exists, only return src/ paths; otherwise only return root paths
- Fixes inconsistency where dev loaded from root even when src/ existed

Co-Authored-By: Jiwon Choi <devjiwonchoi@gmail.com>
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@ijjk
Copy link
Member

ijjk commented Aug 10, 2025

Allow CI Workflow Run

  • approve CI run for commit: ba60fce

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

@ijjk
Copy link
Member

ijjk commented Aug 10, 2025

Allow CI Workflow Run

  • approve CI run for commit: 1598003

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

Comment on lines +1793 to +1801
if (hasSrcDir) {
files.push(
path.join(folder, `src`, `${INSTRUMENTATION_HOOK_FILENAME}.${extension}`)
)
} else {
files.push(
path.join(folder, `${INSTRUMENTATION_HOOK_FILENAME}.${extension}`)
)
}
Copy link

@vercel vercel bot Aug 10, 2025

Choose a reason for hiding this comment

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

The function now only returns instrumentation hook files from either the root OR src directory, but Next.js is designed to support both locations simultaneously.

View Details

Analysis

The getPossibleInstrumentationHookFilenames function has been modified to only return file paths from one location (either root or src directory) based on whether the src directory exists. This breaks the intended functionality because:

  1. Multiple functions confirm both locations should be valid: Other functions in the same file like isInstrumentationHookFile (lines 1776-1777) and isInstrumentationHookFilename (lines 309-310) clearly show that instrumentation hooks are valid in both /instrumentation and /src/instrumentation locations simultaneously.

  2. Function name implies all possibilities: The function is named getPossibleInstrumentationHookFilenames (emphasis on "Possible"), suggesting it should return all potential locations where the files could exist.

  3. Original behavior was correct: The original implementation returned both paths for each extension, allowing the consumer to check all possible locations.

  4. Potential missed files: If a user has instrumentation files in both the root and src directories, the new logic will only detect files in one location, potentially missing important instrumentation hooks.

The function is used in file watching scenarios where all possible paths should be monitored, not just paths from one directory.


Recommendation

Revert the logic to return both possible paths for each extension, similar to the original implementation:

export function getPossibleInstrumentationHookFilenames(
  folder: string,
  extensions: string[]
) {
  const files = []
  for (const extension of extensions) {
    files.push(
      path.join(folder, `${INSTRUMENTATION_HOOK_FILENAME}.${extension}`),
      path.join(folder, `src`, `${INSTRUMENTATION_HOOK_FILENAME}.${extension}`)
    )
  }
  return files
}

If optimization is desired, the file existence checking should be done by the consumer of this function, not within the function that's supposed to return all "possible" locations.

- Import existsSync from 'fs' alongside existing fs import
- Remove inline require('fs') statement that was causing build conflicts
- Maintains the same logic for prioritizing src/ directory when it exists

Co-Authored-By: Jiwon Choi <devjiwonchoi@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant