Skip to content

Comments

Fix ReferenceError: process is not defined in native browser ESM#3438

Merged
mattgperry merged 3 commits intomainfrom
claude/fix-issue-3417-kQuRg
Jan 5, 2026
Merged

Fix ReferenceError: process is not defined in native browser ESM#3438
mattgperry merged 3 commits intomainfrom
claude/fix-issue-3417-kQuRg

Conversation

@mattgperry
Copy link
Collaborator

Guard process.env.NODE_ENV access in errors.ts to prevent ReferenceError when importing motion-utils as a native ES module in browsers without bundlers. This adds a typeof check for process before accessing process.env.NODE_ENV.

Fixes #3417

Guard process.env.NODE_ENV access in errors.ts to prevent ReferenceError
when importing motion-utils as a native ES module in browsers without
bundlers. This adds a typeof check for process before accessing
process.env.NODE_ENV.

Fixes #3417
@claude
Copy link

claude bot commented Jan 5, 2026

Code Review - PR #3438

Summary

This PR fixes a critical bug where importing motion-utils as a native ES module in browsers without bundlers throws ReferenceError: process is not defined. The fix adds proper guards around process.env.NODE_ENV access.

✅ Strengths

  1. Correct Fix: The typeof process !== "undefined" check is the standard approach for handling Node.js globals in browser environments
  2. Minimal Change: The fix is surgical and only touches the problematic code
  3. Optional Chaining: Using process.env?.NODE_ENV adds an extra layer of safety
  4. Preserves Functionality: The fix maintains the original behavior (dev warnings only in development) while enabling browser ESM compatibility

🔍 Code Quality

Good:

  • Follows JavaScript best practices for environment detection
  • Maintains backward compatibility with bundlers
  • No breaking changes to existing functionality

Considerations:

  • The multi-line condition formatting is appropriate given the added complexity

🐛 Potential Issues

None identified. The fix is straightforward and addresses the root cause properly.

⚡ Performance

No concerns. The typeof check is performed once at module initialization time, so there's zero runtime overhead.

🔒 Security

No concerns. The change is defensive and reduces the risk of runtime errors in browser environments.

🧪 Test Coverage

Suggestion: While the fix is simple, it would be valuable to add a test case that validates the module can be imported in a browser-like environment where process is undefined. This could be a simple unit test:

// packages/motion-utils/src/__tests__/errors.test.ts
describe("errors module", () => {
    it("should not throw when process is undefined", () => {
        const originalProcess = global.process
        try {
            // @ts-ignore
            delete global.process
            
            // Re-import or re-evaluate the module initialization logic
            expect(() => {
                // Module import should not throw
            }).not.toThrow()
        } finally {
            global.process = originalProcess
        }
    })
})

However, this is optional since:

  1. The fix is trivial and low-risk
  2. The original issue reporter can verify the fix in their real-world browser environment
  3. The pattern used is well-established in the JavaScript ecosystem

🏗️ Build Configuration

Looking at rollup.config.mjs (packages/motion-utils/rollup.config.mjs:16), I notice the build uses @rollup/plugin-replace to replace process.env.NODE_ENV at build time for UMD builds. This fix ensures the ES module build also works correctly when the replacement doesn't occur (native browser ESM scenario).

✨ Recommendation

APPROVE - This is a solid fix that resolves issue #3417. The implementation is correct, minimal, and follows established patterns for handling Node.js globals in cross-platform JavaScript code.

The fix properly enables motion-utils to work as a native ES module in browsers while maintaining full backward compatibility with bundled environments.

Tests cover warning and invariant functions exported from errors.ts,
including verification that error code links are properly formatted.
@claude
Copy link

claude bot commented Jan 5, 2026

Code Review

Summary

This PR fixes a critical bug where importing motion-utils as a native ES module in browsers (without bundlers) throws ReferenceError: process is not defined. The fix adds proper guards around process.env.NODE_ENV access.


✅ Strengths

  1. Correct Fix: The guard typeof process !== "undefined" && process.env?.NODE_ENV !== "production" properly handles the browser ESM scenario.

  2. Uses Optional Chaining: The .env? optional chaining is a good defensive measure, though technically process.env should exist if process exists.

  3. Good Test Coverage: The new test file covers:

    • Warning functionality
    • Invariant functionality
    • Error code link formatting
    • Module initialization guard
  4. Minimal Surface Area: The fix is targeted to the specific issue without over-engineering.


🔍 Potential Issues

Critical: Other Packages May Have the Same Bug

The fix only addresses motion-utils/errors.ts, but other packages likely have the same issue:

  • motion-dom/src/value/index.ts:210 - Unguarded process.env.NODE_ENV check
  • motion-dom/src/animation/JSAnimation.ts:111 - Unguarded process.env.NODE_ENV check
  • Multiple files in framer-motion package

Since motion-dom and framer-motion also export native ES modules ("import": "./dist/es/index.mjs"), they will have the same problem. Users importing these packages directly as ESM will hit the same error.

Recommendation: Audit all module-level process.env.NODE_ENV checks across the monorepo and apply the same guard pattern.

Minor: Test Coverage Gap

The test at line 77-88 attempts to verify the process guard but has a limitation:

describe("process guard", () => {
    it("should handle undefined process gracefully", () => {
        // We can't directly test module initialization...
        expect(typeof warning).toBe("function")
        expect(typeof invariant).toBe("function")
    })
})

This doesn't actually test the guard because process is always defined in the Node.js test environment. The test just confirms the module loads in Node. Consider adding a comment that this test serves as documentation rather than true verification, or consider an E2E test with a real browser ESM import.


📋 Code Quality

  • Style Compliance: ✅ Follows repo conventions (named exports, interfaces, no var)
  • No Security Issues: ✅ No vulnerabilities introduced
  • Performance: ✅ No performance impact (module-level check, runs once)
  • Backwards Compatibility: ✅ Fully compatible, no breaking changes

🎯 Recommendations

  1. Before Merging: Search for all unguarded process.env.NODE_ENV checks at module level across packages and apply the same fix
  2. Consider: Adding a lint rule or build-time check to catch future unguarded process access
  3. Optional: Update the test comment to clarify it's documentation of intent rather than verification of the guard

Final Verdict

Approve with recommendations - The core fix is correct and solves the reported issue. However, the same bug likely exists in other packages (motion-dom, framer-motion) and should be addressed comprehensively.

The fix pattern should be:

if (typeof process !== "undefined" && process.env?.NODE_ENV !== "production") {
    // dev-only code
}

Great work identifying and fixing this ESM compatibility issue! 🎉

@claude
Copy link

claude bot commented Jan 5, 2026

Code Review

Summary

This PR fixes a critical bug where native ES module imports in browsers fail with ReferenceError: process is not defined. The fix adds proper guards for process access in errors.ts.

✅ Strengths

  1. Correct Fix: The typeof process !== "undefined" check is the standard pattern for Node.js global detection in universal JavaScript code.

  2. Optional Chaining: Using process.env?.NODE_ENV adds an extra layer of safety in case process.env is somehow undefined (though unlikely with the typeof check).

  3. Good Test Coverage: The new test file covers:

    • Warning behavior (both true/false conditions)
    • Invariant behavior (error throwing)
    • Error code link formatting
    • Process guard (module initialization)
  4. Proper Fallback: The code maintains backward compatibility by keeping no-op functions when dev tools aren't available.

  5. Minimal Change: The fix is surgical - only adds necessary guards without refactoring unnecessarily.

📝 Minor Observations

  1. Test Limitation (acknowledged in code): The "process guard" test at lines 75-87 can't directly test the module initialization behavior in a browser-like environment without process. The test acknowledges this limitation in comments. This is acceptable since:

    • True testing would require complex test environment setup
    • The fix follows established patterns
    • Manual testing was likely performed (as per the issue reproduction steps)
  2. Production Behavior: In production builds, these functions remain no-ops regardless of process availability. This is correct and expected behavior.

🎯 Code Quality Assessment

  • Style Compliance: ✅ Follows CLAUDE.md conventions (arrow functions, strict equality)
  • Type Safety: ✅ Maintains existing TypeScript types
  • Performance: ✅ No runtime overhead - checks happen once at module initialization
  • Security: ✅ No security concerns introduced
  • Browser Compatibility: ✅ Fixes the native ESM issue completely

Recommendation

LGTM ✅ - This PR is ready to merge.

The fix is correct, minimal, and properly tested. It resolves issue #3417 by ensuring motion-utils can be imported as native ES modules in browsers without bundlers.

@mattgperry mattgperry merged commit 8357951 into main Jan 5, 2026
4 checks passed
@mattgperry mattgperry deleted the claude/fix-issue-3417-kQuRg branch January 5, 2026 10:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] ReferenceError: process is not defined in native ES module usage

2 participants