Skip to content
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

import.meta.runtime #19

Open
guybedford opened this issue Feb 17, 2025 · 18 comments
Open

import.meta.runtime #19

guybedford opened this issue Feb 17, 2025 · 18 comments

Comments

@guybedford
Copy link
Contributor

The runtime keys gives useful information about the environment to the resolver, but there is currently no way to inspect this information from within the runtime environment.

I understand this proposal is moving to a more general package.json spec, but before we do that, I wanted to discuss the possibility of an import.meta.runtime object which would expose a frozen bag of booleans corresponding to the runtime key environment.

Would be useful for platform checks:

if (import.meta.runtime.node) { ... }

As well as dev / production checks:

if (import.meta.runtime?.development) { ... }

Having a standard like this would get support between Node.js and tools - of course we can't get support in browsers, but that might be acceptable having this as a build time inlining. And perhaps the browser does even define import.meta.runtime = { browser: true } one day.

@JakobJingleheimer
Copy link

I like this. But not ….runtime.node. If we could have a not-insane version of user agent, that would be great. It doesn't necessarily need to be so specific as to identify the UA, but server vs browser could save a lot of headache. And a standardise replacement for NODE_ENV 🥹 basically a lot of what is on node:process could be well standardised.

@guybedford
Copy link
Contributor Author

The nice thing about the runtime.node definition is that it follows the clearly defined meaning of the runtime keys which are already specified. User agents are notorious for backwards compat. For example Deno wants to present itself as Node.js these days so is actually using the node condition set to true - so would get runtime.node === true and runtime.deno === true which is sensible and a better way of handling the "every user agent eventually copies Chrome's structure" problem.

@jkrems
Copy link

jkrems commented Feb 17, 2025

As well as dev / production checks:

I assume this would expand the definition of "runtime keys" which currently is only a subset of "conditions"?

@guybedford
Copy link
Contributor Author

Good point - perhaps this is wider in scope than runtime keys then. The question then would be where to specify this in WinterTC though...

@pi0
Copy link

pi0 commented Feb 17, 2025

For example Deno wants to present itself as Node.js these days so is actually using the node condition set to true - so would get runtime.node === true and runtime.deno === true which is sensible and a better way of handling the "every user agent eventually copies Chrome's structure" problem.

Other runtimes being able to tell they are "like" Node.js, can be both beneficial (in a way libraries can then depend on Node.js APIs) but also very confusing in other use cases when actually libraries need to check "real" Node.js (to depend on native bindings for example). unjs/std-env#100 for reference, we used to have flags like isNode === true (~runtime.meta.node) which got the interpretation much harder vs runtime === "node" that is now preferred

May I suggest that we split this into two proposals:

  • Runtimes to strictly flag their "identity"/name (something like runtime keys)
  • Runtimes to flag their "compatibility" (something like current proposal, as multiple flags or UA-like, I think flags like this are better also for build-tools and static analyzes)

@guybedford
Copy link
Contributor Author

@pi0 how would the described split you mention handle development and production conditions? We could solve that issue separately as well and just specify an import.meta.development or import.meta.production, but it's nice to get other resolution conditions "for free". Perhaps a different scoping is the way to go though.

@pi0
Copy link

pi0 commented Feb 17, 2025

import.meta.development + import.meta.production are special in that they are mutually exclusive, explicit and orderless flags.

Conditions like node, deno, workerd, web need to be either explicit (import.meta.runtime.name: "deno" for example or at least specified in order like import.meta.runtime.compatibility: ["deno", "node"]

@ljharb
Copy link
Member

ljharb commented Feb 18, 2025

I'm very confused why we'd want to effectively replicate the useragent nightmare of the web, on the server. A world in which "node" is true, but it's not node, does not sound like a good one - nor does a world where people are explicitly writing code intended for node, but it still runs on "not node", potentially doing the wrong thing. (replace "node" with any other runtime, same applies)

What are the concrete use cases here that can't be solved by actual feature detection?

@jkrems
Copy link

jkrems commented Feb 18, 2025

I have seen an unfortunate number of packages/guides using conditions "wrong", e.g. doing {node, default} instead of {browser, default} which is more likely to work. So now deno/bun need to work around those packages somehow.

@guybedford
Copy link
Contributor Author

guybedford commented Feb 18, 2025

There are two separate use cases here:

  1. A universal replacement for the process.env.NODE_ENV === 'production' check which is Node.js specific and a pattern which is common in the npm ecosystem which should ideally have a cross-platform replacement
  2. A "runtime" runtime keys lookup - making information available to the resolver available to users. For example fastly is a runtime key for the Fastly platform, and we wouldn't need to design our own user agent to be able to support checking if you are on the already-defined Fastly runtime key at runtime.

(1) is the primary use case I am personally seeking to solve here right now - so we could separately specify a universal import.meta.development or import.meta.production, but it's worth going through the discussion on a more general conditions runtime bag of bools since this is already standardized what those key definitions would be.

@jedwards1211
Copy link

If you go down that road then it expands the concept of import conditions rather broadly, from something specific to changing module resolution, to a hard-to-define concept of ambient static flags about the build/runtime environment

@ljharb
Copy link
Member

ljharb commented Feb 18, 2025

@jkrems and i'd have hoped that the lesson we learned from the web is, you shouldn't work around them, you should evangelize them to do things properly instead, before the problem gets too widespread.

@jedwards1211
Copy link

some kind of flags about the runtime or build-time environment seem necessary but it doesn't seem quite right for them to live under import since some flags may have nothing to do with module resolution

@jkrems
Copy link

jkrems commented Feb 18, 2025

@jkrems and i'd have hoped that the lesson we learned from the web is, you shouldn't work around them, you should evangelize them to do things properly instead, before the problem gets too widespread.

I agree!

Although I'll also add that "working by default" beats "evangelizing and making everybody pick correctly". Which is why I would strongly recommend not having both production and development if such an API is added but only a development condition. (Longer reasoning in https://github.com/jkrems/md/blob/main/import-meta-debug.md)

@guybedford
Copy link
Contributor Author

I feel like there are two major paths forward here at this current point in time:

  1. Seek to specify a unified more general conditions object based on a new specification (wider scope that runtime keys) - import.meta.conditions or something, based on the existing definitions
  2. Separately focus on the production / development problem and the user agent problem

I still think there is merit in tackling (1) because it provides a flexible more general mechanic that only needs to be implemented once in build tools as an addition to exsiting conditions handling, and this bag of bools effectively already exists ambiently at linking time to be able to inline.

Are there strong arguments against this direction?

@ljharb
Copy link
Member

ljharb commented Feb 18, 2025

@guybedford what are the concrete use cases that path 1 solves? It's hard to understand what a general solution would entail without a full grasp of the specific ones.

@panva
Copy link

panva commented Feb 18, 2025

Would be useful for platform checks

So long as there are strict rules for not posing as other platforms, otherwise it's as useless as runtime-keys themselves for module authors, see #18

@wesleytodd
Copy link

wesleytodd commented Feb 18, 2025

A universal replacement for the process.env.NODE_ENV === 'production' check which is Node.js specific and a pattern which is common in the npm ecosystem which should ideally have a cross-platform replacement

This is a pattern which has caused many hours of wasted time due to confusion, poor hygiene by library authors, and poor guidance on it's use. The work done with this flag is nearly always able to be done without a global switch, especially one implemented as an environment variable, and is better off with fine grained control. It should be deprecated and removed, not replaced with something to spread this pain around further.

I don't have a horse in this race beyond that, but when I saw that was part of the driver for this I wanted to share in case it helped avoid another mistake like NODE_ENV.

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

No branches or pull requests

8 participants