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

feat: additive global context for npm packages (first pass) #1

Open
wants to merge 15 commits into
base: branch_v4.8.3
Choose a base branch
from

Conversation

dsherret
Copy link
Member

@dsherret dsherret commented Oct 4, 2022

This is hopefully a temporary fork while we investigate npm specifiers in Deno. In the future, we hope a general solution to this problem can be upstreamed to TypeScript. Note the code here is written in a way to make maintaining a fork easier and wouldn't be what would be upstreamed.

Problem A - Need for additive node-specific global context

With npm specifiers, Deno offers a portal into npm modules. So we have "deno code" and "node code" running, which has slightly different behaviour. For example, if you use a setTimeout in Node code, it will return a NodeJS.Timeout, but if you use setTimeout in Deno it will return a number as in browsers.

  1. There are Node global overrides like setTimeout (different return type).
  2. There are Node specific globals like the process global.
  3. There are Node native modules like "fs" that are only allowed to be imported from Node code. For example, importing import * as fs from "fs" should error in Deno code, but succeed in Node code.
  4. Node code can add to the list of globals used by Deno code. These should not fail type checking. So for example, people could import import "npm:some-package/set-globals" which might set globals.
  5. Users should be able to specify a specific version of @types/node they want to use, if desired, or some other package.

Solution

The solution here is to manually update TypeScript's type checker to have an additional override set of globals for Node along with introducing a nodeGlobalThisSymbol to be the symbol for globalThis in node files. There is a list of type names that get added to the node code and those symbols stay over there.

I'm sure the code as-is today will have issues as I haven't spent much time in TypeScript's codebase. We will investigate and fix anything going forward.

Example types in npm packages

image

image

Example types in deno code

image

image

Problem B - Need for mapping npm specifier to ambient module

The TypeScript Node ecosystem seems reliant on bare specifier mapping. For example, the stripe npm package provides built-in types, but instead of exporting the types it declares an ambient module:

https://github.com/stripe/stripe-node/blob/deb7bb54c82115d04ad8ba246d8a0ffb0b7c885c/types/2022-08-01/index.d.ts#L130

In my opinion, it's incorrect to do this because then it is impossible to alias the package in a package.json and get its types (ex. doing "stripe-alias": "npm:stripe@^10.13.0" in a package.json, then importing "stripe-alias" will error). That said, several packages do this and if you search DefinitelyTyped for "declare module" you will see many packages that also do this or extend other package's declarations via a bare module specifier.

Solution

The solution implemented in this PR is that when a modules resolves to a symbol with zero exports and the specifier is an npm package reference, it will then resolve to the ambient module for that npm package. For example, say someone does import Stripe from "npm:[email protected]", it will resolve to the types/2022-08-01/index.d.ts file, find the symbol has no exports, then fallback to the ambient "stripe" module.

This has some obvious issues, like if someone imports from "npm:stripe@9" in one part of their code and then imports from "npm:stripe@10" in another part, but I would suspect a user doing this is a rare scenario.

The current implementation is very basic. We'll need to improve this in the future.

image

@dsherret dsherret marked this pull request as draft October 4, 2022 21:38
@dsherret dsherret changed the title feat: separate global context for npm packages (first pass) feat: additive isolated global context for npm packages (first pass) Oct 4, 2022
@dsherret dsherret changed the title feat: additive isolated global context for npm packages (first pass) feat: additive global context for npm packages (first pass) Oct 4, 2022
@dsherret dsherret changed the base branch from branch_v4.8.4 to branch_v4.8.3 October 17, 2022 21:26
@dsherret dsherret marked this pull request as ready for review December 6, 2022 22:50
@dsherret dsherret mentioned this pull request Dec 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
1 participant