{"action":"created","issue":{"url":"https://api.github.com/repos/nodejs/modules/issues/80","repository_url":"https://api.github.com/repos/nodejs/modules","labels_url":"https://api.github.com/repos/nodejs/modules/issues/80/labels{/name}","comments_url":"https://api.github.com/repos/nodejs/modules/issues/80/comments","events_url":"https://api.github.com/repos/nodejs/modules/issues/80/events","html_url":"https://github.com/nodejs/modules/issues/80","id":321735010,"node_id":"MDU6SXNzdWUzMjE3MzUwMTA=","number":80,"title":"Use Case #42 Discussion","user":{"login":"weswigham","id":2932786,"node_id":"MDQ6VXNlcjI5MzI3ODY=","avatar_url":"https://avatars3.githubusercontent.com/u/2932786?v=4","gravatar_id":"","url":"https://api.github.com/users/weswigham","html_url":"https://github.com/weswigham","followers_url":"https://api.github.com/users/weswigham/followers","following_url":"https://api.github.com/users/weswigham/following{/other_user}","gists_url":"https://api.github.com/users/weswigham/gists{/gist_id}","starred_url":"https://api.github.com/users/weswigham/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/weswigham/subscriptions","organizations_url":"https://api.github.com/users/weswigham/orgs","repos_url":"https://api.github.com/users/weswigham/repos","events_url":"https://api.github.com/users/weswigham/events{/privacy}","received_events_url":"https://api.github.com/users/weswigham/received_events","type":"User","site_admin":false},"labels":[{"id":831674587,"node_id":"MDU6TGFiZWw4MzE2NzQ1ODc=","url":"https://api.github.com/repos/nodejs/modules/labels/discussion","name":"discussion","color":"d93f0b","default":false},{"id":831667756,"node_id":"MDU6TGFiZWw4MzE2Njc3NTY=","url":"https://api.github.com/repos/nodejs/modules/labels/interoperability","name":"interoperability","color":"7eeac3","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":42,"created_at":"2018-05-09T21:36:02Z","updated_at":"2018-05-31T17:29:54Z","closed_at":null,"author_association":"NONE","body":"At today's meeting, some people expressed interested in a more complete description of the context and background around use case 42. Here's the usecase, for reference:\r\n\r\n> Rachel is a TypeScript user who is importing some JavaScript code that uses CommonJS. She uses declaration files that were written on DefinitelyTyped, but were authored as ES module top-level exports as so:\r\n>```\r\n>export function foo() {\r\n>}\r\n>export function bar() {\r\n>}\r\n>```\r\n>When she imports them from TypeScript, she gets code-completion on the namespace import for `foo` and `bar`\r\n>```\r\n>import * as thing from \"some-package\";\r\n>thing./* completions here*/\r\n>```\r\n>When she compiles her code to run on either the 'commonjs' or 'esnext' module targets, she expects both to run correctly.\r\n\r\nSo, first, some background. TypeScript has these things called \"declaration files\". They're additional metadata about a `.js` file that includes additional type information for a module (written in files with a `.d.ts` extension); this is how `vscode` can provide good completions for things like [`lodash`](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/lodash) and [`jquery`](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/jquery).  They usually look something like this:\r\n```ts\r\n// Type definitions for abs 1.3\r\n// Project: https://github.com/IonicaBizau/node-abs\r\n// Definitions by: Aya Morisawa <https://github.com/AyaMorisawa>\r\n// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped\r\n/**\r\n * Compute the absolute path of an input.\r\n * @param input The input path.\r\n */\r\ndeclare function Abs(input: string): string;\r\nexport = Abs;\r\n```\r\nor [this](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/22721ffe2ef48f280b52ea06cd5485fff7baa1e2/types/history/index.d.ts):\r\n```ts\r\n// ... some more definitions\r\nexport * from \"./createBrowserHistory\";\r\nexport * from \"./createHashHistory\";\r\nexport * from \"./createMemoryHistory\";\r\nexport { createLocation, locationsAreEqual } from \"./LocationUtils\";\r\nexport { parsePath, createPath } from \"./PathUtils\";\r\nexport { createBrowserHistory, createHashHistory, createMemoryHistory };\r\n```\r\nthat is to say, normal es6 syntax with the addition of `export=` (to describe the commonjs only pattern of replacing the namespace object) and type annotations, without any expressions or blocks. Someone went and put in the effort to write these type definitions at some point in the past, and there's now a community of people authoring these and keeping them up-to-date. The hub of that community is [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) - every definition file published there is automatically published to the `@types` npm namespace under the same name as the package it corresponds with - this means that, for example, `jquery` has types available via `@types/jquery`. It's a kind of crowd-sourced documentation/metadata store.\r\n\r\nSo, the dilemma. The `typescript` compiler (and by extension the `vscode` js language service, as it is really just the `typescript` compiler behind a thin facade) follows `node`'s module reolution scheme to find js and/or ts files. In addition, it will also look for an adjacent `.d.ts` file to provide type information for a `.js` file, and, failing that, an `@types/packagename` package with a declaration file to provide the types. (Failing either of those in some configurations it will fall back to the actual JS, if it is able to, but this is costly - there's a _lot_ of JS and it needs to be processed a _lot_ to get good type data from it, which is why declarations are preferred.) We have two unique issues to deal with in the `esm` transition, both of which come into play here in this use-case. The simpler one is emit - providing a node-esm emit target that interoperates decently. The more complicated one is typechecking.\r\n\r\nTo start with typechecking (for both js files and ts ones): You'll note in my description of declaration files above, I didn't mention anything about any encoding of the library's available module format(s). This is important - we expect that no matter if you're targeting `cjs` or `amd` or `esnext` that the same declaration file will be able to accurately represent the module. This is critical, as it turns out, because some of our consumers will target `esnext` with us, but then actually transpile to `cjs` using another bundling tool, like rollup or webpack (retaining the es6 style imports for dead code elimination). We (strongly) operate under the assumption that interop between various module formats is invisible to the end-user - this carries into the js editing experience, where we assume that weather you wrote `import * as x from \"lodash\"` or `const x = require(\"lodash\")` it will produce roughly the same module[1], and have the same members when you look for completions on `x`. Now, clearly we're capable of changing this assumption (likely behind a flag, but w/e), but this would (will?) fracture our ecosystem of existing type definition files; anything already written would need to only be interpreted as a `cjs` package, and we'd have to introduce a marker (file extension, pragma, or otherwise) to flag a declaration file as `node-esm` so that we can reject the old one and only accept the other for resolution depending on the exact interop scheme. It's not exactly pretty, and goes about as far away from a single \"universal\" declaration file as you can get (and, naturally, starts to require extra maintenance work to maintain the doubled APIs). Compound that with the fact that nobody usually bothers to tell their editor anything about the files they're working with (ie, will this  random `.js` file be targeting node-esm, esm, or cjs? - at least at first), and we might really have to start arbitrarily guessing about what types an `import` should actually map to on disk, depending on any exact interop scheme, which is no good from a type safety perspective.\r\n\r\nOur emit issues are more clear, and mostly center around exactly how interop might work. The `typescript` compiler, being a transpiler with multiple supported output formats, allows you to write the same es6-style input code and transpile it to either `cjs` or `esm` module formats (or `amd` or `umd` or `systemjs`). It will also auto-generate a declaration file for you. Generally, it is expected that your code will function the same way when targeting any of these module runtimes and present the same interface to consumers who can understand the format (and the same declaration file is currently produced for all of them). Some constructs (like export namespace assignment) aren't supported on some emit targets (ie, `esnext`), but otherwise interop is generally expected (after all, that's a big part of a transpiler's job). Node's interop scheme, if not fully transparent, would probably require us to emit helpers/perform transforms mapping from the more transparent interop we support today to any more explicit form of module system interop supported by the platform, thus requiring a new, independent module target, different from normal un-transformed `esnext` modules. Failing that, it would require a flag that at least alters our checking and resolution to only allow any stricter platform interop scheme, which would, naturally, not be able to be the default so as to not break long time users.\r\n\r\nWe also have relatively strong compatibility needs, since our service needs to keep working on code that was written 1, 2, 3 years ago, as nobody wants to launch their editor on their legacy codebase and just be greeted with a bunch of confused warnings and incorrect types, which necessitates a lot of changes be non-default flags. Our stance is, typically, we only intentionally introduce \"breaks\" if said break is identifying real problems in your code (ie, you were accessing a property which could never actually exist, but we didn't catch before). And then we'll usually have a flag to turn off the new check. Even for the `3.0` release that we have a milestone up for now, we don't have any really major breaks in the backlog - just larger features (it's more of a pragmatic answer to \"what comes after `2.9` when incremented by `.1`\" than a semver major release).\r\n\r\n[1]We have a flag for placing the module on an es6-style import's `default` and disallowing calling namespace objects (`esModuleInterop`), to align our typecheck and emit with babel's emit, however this isn't currently our default for fear of breaking longtime users.\r\n\r\ncc @DanielRosenwasser I hope I've explained your concerns, but you should feel free to chime in."},"comment":{"url":"https://api.github.com/repos/nodejs/modules/issues/comments/393610844","html_url":"https://github.com/nodejs/modules/issues/80#issuecomment-393610844","issue_url":"https://api.github.com/repos/nodejs/modules/issues/80","id":393610844,"node_id":"MDEyOklzc3VlQ29tbWVudDM5MzYxMDg0NA==","user":{"login":"SMotaal","id":849613,"node_id":"MDQ6VXNlcjg0OTYxMw==","avatar_url":"https://avatars3.githubusercontent.com/u/849613?v=4","gravatar_id":"","url":"https://api.github.com/users/SMotaal","html_url":"https://github.com/SMotaal","followers_url":"https://api.github.com/users/SMotaal/followers","following_url":"https://api.github.com/users/SMotaal/following{/other_user}","gists_url":"https://api.github.com/users/SMotaal/gists{/gist_id}","starred_url":"https://api.github.com/users/SMotaal/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/SMotaal/subscriptions","organizations_url":"https://api.github.com/users/SMotaal/orgs","repos_url":"https://api.github.com/users/SMotaal/repos","events_url":"https://api.github.com/users/SMotaal/events{/privacy}","received_events_url":"https://api.github.com/users/SMotaal/received_events","type":"User","site_admin":false},"created_at":"2018-05-31T17:29:54Z","updated_at":"2018-05-31T17:29:54Z","author_association":"NONE","body":"@bmeck trust me, I have grown to be a big subscriber to your \"push back on dynamically mutating\" which obviously was not how I felt about it around the same time last year. I am actually interested in doubling down on this idea which can only be possible by offering abstractions that empower users to accomplish things in a controlled way."}}
{"actor":{"display_login":"SMotaal"}}