…ocal development (#7325)
* feat: pass mutated config environment variables to functions during local development
## Summary
Fixes environment variables from build event handlers not being passed to functions during local development. Static config environment variables from `netlify.toml` were already working via global process environment injection, but variables added by plugins via build event handlers were not.
## Problem
Build event handlers (like `onPreDev`) could successfully mutate the config to add environment variables to `config.build.environment`, but these **mutated** environment variables were not available to functions in `process.env` during local development.
**What was working:**
- Static environment variables from `netlify.toml` → `cachedConfig.env` → global `process.env` → functions ✅
- Build event handler mutations → `config.build.environment` ✅
- Process env override behavior ✅
**What was broken:**
- Mutated config environment variables → functions ❌
## Root Cause
Static config environment variables work because they're included in `cachedConfig.env` and injected into the global `process.env` via `injectEnvVariables()`. However, mutated config environment variables from build event handlers are only stored in `config.build.environment` and were never being passed to functions.
The `NetlifyFunction.invoke()` method was passing an empty `environment` object to the runtime, so functions only had access to the global `process.env` (which contained static config vars) but not the mutated config environment variables.
## Solution
Instead of injecting mutated config into the global `process.env` (which could affect other parts of the system), we:
- Extract environment variables from `config.build.environment` in `NetlifyFunction.invoke()`
- Pass them directly to the function runtime via the `environment` parameter
- The runtime injects them into the function's specific `process.env`
- Maintain existing precedence: process env variables override config env variables
## Key Changes
### Core Implementation
- **`src/lib/functions/netlify-function.ts`**: Extract environment variables from mutated config and pass to runtime
- **`src/lib/functions/runtimes/js/index.ts`**: Update V1 function path to accept and use environment variables
### Testing
- **`tests/integration/commands/dev/functions.test.ts`**: Significantly expanded test coverage:
- **NEW**: Static config environment variables for V1 functions
- **NEW**: Build event handler environment variables for V1 functions
- **NEW**: Static config environment variables for V2 functions *(first V2 function env var tests)*
- **NEW**: Build event handler environment variables for V2 functions *(first V2 function env var tests)*
- **NEW**: Environment variable precedence for both V1 and V2 functions *(first build event handler mutation tests)*
**Note**: While static config environment variables were already tested in `dev.config.test.ts` for V1 functions, this PR adds the first comprehensive test coverage for V2 functions and build event handler mutations.
## Technical Details
This approach is better than global environment injection because:
- It doesn't pollute the global process environment
- It only affects the specific function being invoked
- It maintains proper precedence handling
- It works for both V1 and V2 function runtimes
## Use Case
This enables plugins to add environment variables via build event handlers:
```javascript
// Plugin build event handler
export const onPreDev = ({ netlifyConfig }) => {
netlifyConfig.build.environment.MY_PLUGIN_VAR = 'value'
}
// Function can now access the mutated config env var during local dev
export const handler = async (event, context) => {
console.log(process.env.MY_PLUGIN_VAR) // 'value' ✅ (was undefined ❌)
return { statusCode: 200, body: 'OK' }
}
```
## Test Coverage
✅ V1 Functions - Static config and build event handler environment variables
✅ V2 Functions - Static config and build event handler environment variables
✅ Environment variable precedence - Process env overrides config env for both function types
✅ Backwards compatibility - All existing functionality preserved (16 existing tests still pass)