-
Notifications
You must be signed in to change notification settings - Fork 19
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
Avoid assigning globalThis.process when not defined. #94
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Follow-up to #91. Some websites use the existence of a global process object to feature-detect the Node.js host environment, so it's not safe for this package to initialize globalThis.process when it does not already exist.
jcreighton
approved these changes
Mar 17, 2021
This export can be imported and assigned to a local variable called `process`, which is convenient because the existing export named `process` would likely be rewritten by bundlers as tsInvariant.process, or collide with a local variable called `process`.
hwillson
added a commit
to apollographql/apollo-client-devtools
that referenced
this pull request
Mar 18, 2021
The issues fixed by #460 didn't quite go far enough. This commit updates to more recent versions of `@apollo/client`, and ultimately `ts-invariant`, to pull in the changes made in apollographql/invariant-packages#94. Fixes #457
benjamn
added a commit
that referenced
this pull request
Mar 18, 2021
I will happily reach for this package again for any code that needs reliable access to the global object (without calling Function), but we no longer need it for the ts-invariant package, after PR #94.
benjamn
added a commit
that referenced
this pull request
Mar 18, 2021
I will happily reach for this package again for any code that needs reliable access to the global object (without calling `Function`), but we no longer need it for the `ts-invariant` package, after PR #94.
benjamn
added a commit
to apollographql/apollo-client
that referenced
this pull request
Jun 3, 2021
As explained in #8266, our use of process.env.NODE_ENV requires those expressions to be either replaced with a string literal by a minifier (common convention in the React ecosystem) or polyfilled globally. We stopped polyfilling process.env globally in the ts-invariant package in apollographql/invariant-packages#94, but @apollo/client is still relying on process.env internally, as is the graphql package. If we want to rid ourselves fully of the drawbacks of process.env.NODE_ENV, we probably ought to stop using it ourselves. Though most developers in the React ecosystem will be using a bundler or minifier that replaces process.env.NODE_ENV at build time, you may not have the luxury of custom minification when loading @apollo/client from a CDN, which leaves only the option of a global process polyfill, which is problematic because that's how some applications detect if the current code is running Node.js (more details/links in #8266). Instead, I believe we can (and must?) stop using process.env.NODE_ENV, and one of many better alternatives appears to be the __DEV__ variable used by React Native, which is much easier to polyfill, since it's a single variable rather than a nested object. Switching to __DEV__ will initially cause a large bundle size regression (+3.5Kb *after* minification and gzip), but that's not technically a breaking change (and thus acceptable for AC3.4), and it should be easy to reconfigure minifiers to replace __DEV__ with true or false (or even just undefined), with sufficient guidance from the release notes. That still leaves the process.env.NODE_ENV check in instanceOf.mjs in the graphql package. Discussion in graphql/graphql-js#2894 suggests the plan is to stop using NODE_ENV altogether, which would be ideal, but that won't happen until graphql@16 at the earliest. To work around the problem in the meantime, I devised a strategy where we polyfill process.env.NODE_ENV only briefly, while we import code that depends on graphql/jsutils/instanceOf.mjs, and then synchronously remove the global polyfill, so it does not permanently pollute the global namespace. This strategy assumes @apollo/client is the first to import the graphql package. If you have other code that imports instanceOf.mjs earlier, and you don't have a process.env.NODE_ENV strategy already, it's your responsibility to make that import work, however you see fit. Apollo Client is only responsible for making sure its own imports of the graphql package have a chance of succeeding, a responsibility I believe my strategy fulfills cleverly if not elegantly. Of course, this charade does not happen if process.env.NODE_ENV is already defined or has been minified away, but only if accessing it would throw, since that's what we're trying to avoid. Although we could do some more work to reduce the bundle size impact of blindly switching to __DEV__, I believe this PR already solves the last remaining hurdles documented in #8266, potentially allowing @apollo/client/core@beta to be loaded from an ESM-aware CDN like esm.run or jspm.io. The default __DEV__ value will be true in those environments, but could be initialized differently by a script/module that runs earlier in the HTML of the page.
benjamn
added a commit
to apollographql/apollo-client
that referenced
this pull request
Jun 4, 2021
As explained in #8266, our use of process.env.NODE_ENV requires those expressions to be either replaced with a string literal by a minifier (common convention in the React ecosystem) or polyfilled globally. We stopped polyfilling process.env globally in the ts-invariant package in apollographql/invariant-packages#94, but @apollo/client is still relying on process.env internally, as is the graphql package. If we want to rid ourselves fully of the drawbacks of process.env.NODE_ENV, we probably ought to stop using it ourselves. Though most developers in the React ecosystem will be using a bundler or minifier that replaces process.env.NODE_ENV at build time, you may not have the luxury of custom minification when loading @apollo/client from a CDN, which leaves only the option of a global process polyfill, which is problematic because that's how some applications detect if the current code is running Node.js (more details/links in #8266). Instead, I believe we can (and must?) stop using process.env.NODE_ENV, and one of many better alternatives appears to be the __DEV__ variable used by React Native, which is much easier to polyfill, since it's a single variable rather than a nested object. Switching to __DEV__ will initially cause a large bundle size regression (+3.5Kb *after* minification and gzip), but that's not technically a breaking change (and thus acceptable for AC3.4), and it should be easy to reconfigure minifiers to replace __DEV__ with true or false (or even just undefined), with sufficient guidance from the release notes. That still leaves the process.env.NODE_ENV check in instanceOf.mjs in the graphql package. Discussion in graphql/graphql-js#2894 suggests the plan is to stop using NODE_ENV altogether, which would be ideal, but that won't happen until graphql@16 at the earliest. To work around the problem in the meantime, I devised a strategy where we polyfill process.env.NODE_ENV only briefly, while we import code that depends on graphql/jsutils/instanceOf.mjs, and then synchronously remove the global polyfill, so it does not permanently pollute the global namespace. This strategy assumes @apollo/client is the first to import the graphql package. If you have other code that imports instanceOf.mjs earlier, and you don't have a process.env.NODE_ENV strategy already, it's your responsibility to make that import work, however you see fit. Apollo Client is only responsible for making sure its own imports of the graphql package have a chance of succeeding, a responsibility I believe my strategy fulfills cleverly if not elegantly. Of course, this charade does not happen if process.env.NODE_ENV is already defined or has been minified away, but only if accessing it would throw, since that's what we're trying to avoid. Although we could do some more work to reduce the bundle size impact of blindly switching to __DEV__, I believe this PR already solves the last remaining hurdles documented in #8266, potentially allowing @apollo/client/core@beta to be loaded from an ESM-aware CDN like esm.run or jspm.io. The default __DEV__ value will be true in those environments, but could be initialized differently by a script/module that runs earlier in the HTML of the page.
benjamn
added a commit
that referenced
this pull request
Jun 16, 2021
Though ts-invariant stopped polyfilling global.process in #94, there's a chance existing applications were accidentally depending on that API, so we can make it easy to install (and then remove!) the global.process polyfill if you still need it: import { install, remove } from "ts-invariant/process" install(); ... remove(); By default, the polyfill will be installed upon importing ts-invariant/process for the first time, so you may not need to call install() in most cases (though it doesn't hurt). Calling remove() cleans up the global namespace, removing the stub global.process object (if ts-invariant/process defined one). This allows you to import other packages that depend on process.env before removing the polyfill, without permanently polluting the global namespace.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Follow-up to #91.
Some websites use the existence of a global
process
object to feature-detect the Node.js host environment, so it's not safe for this package to initializeglobalThis.process
when it does not already exist.The
ts-invariant
package still exports aprocess
object (either the real one in Node, or a stub otherwise) that code can import to avoidReferenceError
s when using theprocess
variable. However, module bundlers may rewrite those variable references to something liketsInvariant.process.env.NODE_ENV
, which may not be correctly replaced by minifiers configured to inline a string literal for theprocess.env.NODE_ENV
expression. In other words, theprocess
export is really just for backwards compatibility, not something we currently encourage.