Skip to content

process.env, globalThis, and typeof process #4075

Closed
@phryneas

Description

@phryneas

This is a writeup to discuss this problem in the GraphQL-WG and the GraphQL-JS-WG. Please give feedback in either of those meetings, or here in the issue, until the next GraphQL-JS-WG meeting, where we want to make a final call on this issue.

Last year, #3887 was released which changed the dev check in src/jsutils/instanceOf.ts from

process.env.NODE_ENV === 'production'

to

globalThis.process?.env.NODE_ENV === 'production'

shortly followed by #3923, which changed it to

globalThis.process && globalThis.process.env.NODE_ENV === 'production'

as some bundlers were choking on the optional chaining syntax.

Since then, various issues and PRs have been opened to change into various other forms.

I'll try to give an overview over problems, potential solutions and their shortcomings here so we can decide on a way forward.

Problems:

1. accessing process.env.NODE_ENV directly

There is a bunch of environments (e.g. ESM in the browser), where accessing process.env directly just crashes, since process is not a variable.

2. accessing globalThis.process.env.NODE_ENV: bundler replacement

Some bundlers do a string replacement of process.env.NODE_ENV, which lead to the code being replaced by invalid JavaScript like globalThis."production".
(Afaik, most of these have been reported in the upstream bundlers at this point in time and they have fixed their regular expressions)

3. accessing process.env.NODE_ENV or globalThis.process.env.NODE_ENV while testing for process, without checking if the env property is set: DOM elements with the id process

If a DOM element with the id process exists (e.g. <span id="process">...</span>), this element will be registered as the global variable process, so it will be accessible as process and globalThis.process - but not have a .env property, so accessing process.env.FOO will crash.

4. testing for process to be present with typeof process: cannot be tree-shaken

Some bundlers (e.g. esbuild) can only replace statements like foo.bar.baz, but not statements like typeof foo as they rely on AST-level replacement and just don't have support for that kind of replacement.

The do not plan to add this to ESBuild: evanw/esbuild#1954 (comment)

Potential solutions:

A) process.env.NODE_ENV === 'production'

This would be a rollback to the original code. It works fine with bundlers, but has problem 1.

B) globalThis.process && globalThis.process.env.NODE_ENV === 'production'

This is the current code. It has problems 2. and 3.

C) globalThis.process && globalThis.process.env && globalThis.process.env.NODE_ENV === 'production'

This is the current code with a fix for 3.
It still has problem 2., but that is mostly solved by bundlers after being out for one year.

D) typeof process && process.env.NODE_ENV === 'production'

A variation of the original code. Problems 3. and 4.

E) typeof process && process.env && process.env.NODE_ENV === 'production'

Another variation of the original. Problem 4.

F) const process = globalThis.process; if (process && process.env && process.env.NODE_ENV)

While this would probably replace fine in some bundlers, other bundlers would detect process as a local variable and never be able to tree-shake it. A variation of problem 4.

A word on bundler code erasing

It should be noted that most bundlers at this point will automatically erase code inside of process.env.NODE_ENV === 'production', but not code inside of globalThis.process.env.NODE_ENV === 'production'.

That said, every bundler can be configured for either of those (as long as we avoid typeof), so I would propose not to take that into account too much.

Suggestion

My personal preference would be to go with C - globalThis.process && globalThis.process.env && globalThis.process.env.NODE_ENV === 'production'.

It will never crash in any environment, and while it is not replaced by most bundlers by default, every bundler can be configured to replace it.

Some bundlers will create invalid JS code from this (blindly string-replacing in the middle of an expression), but that can be considered an upstream bug and has mostly been fixed upstream already.

Outlook

Looking into the future, we will hopefully be able to rely on the development and production exports conditions, so in a future version of graphql, this whole discussion will be mostly obsolete.

This Issue is about finding a short-term fix, not a long-term solution.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions