microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.29k stars 12.39k forks source link

Support ".mjs" input files #27957

Closed mjbvz closed 2 years ago

mjbvz commented 5 years ago

From @Sti2nd on October 7, 2018 16:17

import { timpaniSounds } from "./soundImport.mjs";

In the above example VS code will show all javascript files when writing "./", but not javascript module files. So I didn't see the above file in the list when trying to import it. Not sure if this is a bug or a feature request.

Copied from original issue: Microsoft/vscode#60103

justinfagnani commented 4 years ago

@TimvdLippe the issue with Acorn might be something else.

Importing it works fine if with "module": "commonjs" in my tsconfig, and breaks with "module": "esnext". But when I go into the acorn package and remove the legacy modules, rename the .mjs files to .js, and set "module": "dist/acorn.js",, tsc still fails. So it appears that the .mjs extension might not be the cause in this case.

TimvdLippe commented 4 years ago

@justinfagnani To clarify, we are using Acorn in the browser. Therefore we are not transpiling to commonjs, but ship ES modules instead. Your other points might still be valid. I haven't started renaming the files or modifying the package yet, for legal implications. If that is required, I would have to ask for legal advice on that, I suppose.

justinfagnani commented 4 years ago

@TimvdLippe I was only renaming files to investigate the issue, not as a suggested workaround. My point is that renaming the files didn't work, so the issue is probably not the .mjs extension.

weswigham commented 4 years ago

Looks like acorn ships its own index.d.ts file - meaning we'd never even look at any underlying source files anyway, assuming you're importing via package name, and not, like, directly reaching into the bowels of the package to directly import the distributed mjs file yourself (in which case.... maybe ask that they add a acorn.mjs.d.ts (which does work) to actually provide types for such a usage?).

TimvdLippe commented 4 years ago

We import by URL, not by name. I was not aware of the acorn.mjs.d.ts solution, so I will try that out tomorrow!

justingrant commented 4 years ago

BTW, another use-case to include in this issue is generating .d.ts files from existing code. Currently there doesn't seem to be a way to do this other than by renaming .mjs to .js.

TimvdLippe commented 4 years ago

I finally had time to test it out and I can confirm that the acorn.mjs.d.ts file worked: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2218015

It would be great if documentation could be updated to explicitly call out this possibility, as I reckon other developers will run into similar issues.

frank-dspeed commented 4 years ago

@TimvdLippe it works only partial you still can not jump to the implementation so when you click on your module you always get the d.ts file which is useless

weswigham commented 4 years ago

it works only partial you still can not jump to the implementation so when you click on your module you always get the d.ts file which is useless

You'd need a declaration map shipped with the .d.ts for that; which you're probably not going to get for a hand-authored .d.ts file.

TimvdLippe commented 4 years ago

@frank-dspeed With the .mjs.d.ts file in place, Intellisense and TypeScript checking in VS Code is working. That is iiuc the issue described in OP.

However, given the large amount of responses to this issue and the general confusion/lack of knowledge about .mjs.d.ts, I think additional documentation is required to explain how to make TS/VS Code work with .mjs. For DevTools at least, we were able to make it work.

TimvdLippe commented 4 years ago

For those following along about the .mjs.d.ts: we had a bit of a back-and-forth in Acorn on how to actually making this work without having the need to fully duplicate all types: https://github.com/acornjs/acorn/pull/954 It turns out that this was sufficient:

acorn.mjs.d.ts:

import * as acorn from "./acorn";
export = acorn;

This now works for ESM builds, even though adorn.d.ts is typed as commonJS module. It also doesn't require any change to the tsconfig (which we had a couple of attempts of that did require tsconfig changes).

F1LT3R commented 4 years ago

I am a JavaScript developer who does not use TypeScript, (and is not interested in using TypeScript for front-end code. Sorry).

I do however love VS Code. ❤️

But it really bugs me that there is no import completion for .mjs files.

I see that the all GitHub issues/requests for VS Code to support import completion for .mjs files, get folded into the TypeScript project, and not addressed as part of VS Code itself. I suspect there's a good reason for that, however...

What I would really like to know is whether it's the TypeScript community's intention to only support TypeScript compatible projects, or does it plan to support also Vanilla Web Development too?

As time goes on, it feels as if VS Code is really only focused on TypeScript. It's perfectly fine to do one thing well, but it would be valuable to know what the long term intentions are.

If the TypeScript community are not interested in supporting Vanilla Web Development, please make it clear so that we can find or build alternative tools.

Thanks.

NemoStein commented 4 years ago

@F1LT3R I have the exact same feeling as you. I love VSCode from the bottom of my heart, but now and then I stumble upon a TS problem that I think should be fixed with JS in mind but only TS workarounds get proposed instead.

An unrelated (but similar) TS/JS problem in VSCode I don't have a fix after 4 years is https://github.com/microsoft/TypeScript/issues/5863#issuecomment-445235236

hmenzagh commented 4 years ago

@F1LT3R @NemoStein Can't agree more with you both, it totally boggles me that VSCode (❤️) that sells itself as an "all language IDE" focuses only on TS and is keeping it's users to either retain code updates to stay compatible, or to work on a broken IDE on such basic features ..

I just updates my entire codebase to .mjs & node 14.4 and now have to revert to using esm for compatibility issues

frank-dspeed commented 4 years ago

@hmenzagh do not worry it will get even more bad in future microsoft did buy github & npm so the NodeJS Ecosystem is dead already

bnb commented 4 years ago

As an update, .mjs support for ESM is shipping both in Node.js 12 and 14 unflagged - still experimental, but unflagged. It would be truly wonderful to see TS support it ❤️

frank-dspeed commented 4 years ago

@bnb you did not read that issue right? There are Internal Blockers maybe typescript will never support it

frank-dspeed commented 4 years ago

@mathiasbynens is the person who knows the specs best from all of us here i think the following is blocking TypeScript now.

Blockers

The Patch is the best result we could get the last 3 years. its from @robertrossmann and i did similar stuff to try to get the current version working and it took me over 1 week dedicated work to come to the conclusion that this needs a much bigger project team.

I crafted a patch which enables tsserver to recognise, parse and provide type hints for .mjs files.

This might be useful for people who use the .mjs file extensions for JavaScript files with identical module resolution semantics as CommonJS. Note that this patch does not implement any special behaviour with regard to ES modules and might not be spec-compliant. However, editor integrations which allow using custom-built tsserver implementations will greatly increase developer productivity because suddenly everything seems to work.

@bnb maybe you as a intern of Microsoft can make some one who cares aware of the situation.

ExE-Boss commented 4 years ago

I’ve opened PR https://github.com/microsoft/TypeScript/pull/39840 to implement this.

frank-dspeed commented 4 years ago

If some one does want to test #40068 i have made a patched version ready to use https://github.com/stealify/typescript

npm i -g vscode-typescript@https://github.com/stealify/typescript
# Getting global path
npm root -g

Hit f1 on keyboard open user settings and set

"typescript.tsdk": "{your_global_npm_path}/vscode-typescript/lib"

restart is not needed.

frank-dspeed commented 4 years ago

@weswigham I did now Deep Looked into everything and even examined the complet typescript source it took me Weeks

TypeScript already is able to use .js as ESModule and as CJS Module so this change will not stop anything from working while it would expose all the IntelliSense features inside the editor and compiler…

It makes no diffrence to add that 2 extra file types we simply preserv file attributes as with JSX do you agree? Then i prepare a PR.

we can optional also Introduce AllowCjs, AllowMjs to turn that interop off if some one has trouble with it but i do not see any a Module already Represents a fileName without extension and sure this can lead to None Working builds but so can the usage of .js already.

Overall it is a win to process the Two additional Extensions.

frank-dspeed commented 4 years ago

@weswigham i have prepared a fully working version of the described scenario

https://github.com/microsoft/TypeScript/pull/40068

igorskyflyer commented 3 years ago

2021, still no auto-complete... 😔

exse2 commented 3 years ago

Embrace, Extend, Exterminate - MS doesn't care about ES, only about TypeScript.

justinfagnani commented 3 years ago

@exse2 that's not a helpful comment.

My team has been shipping web-compatible JS modules written in TypeScript for over 4 years now. It works great and tsc supports it with .js imports. There is no hint of bad faith here at all.

exse2 commented 3 years ago

not a helpful comment .... .js ...

No. Your comment is not helpful - We're talking about .mjs not .js

frank-dspeed commented 3 years ago

@exse2 let me give you some context .mjs files are not supported and will probally never get supported while package type module is supported already. hope that helps you to move on with your project.

exse2 commented 3 years ago

Thank you frank-dspeed. That will indeed help me moving on to an editor which properly supports todays non-vendor-lockin EcmaScript Modules every browser speaks.

frank-dspeed commented 3 years ago

@exse2 the browser does not care for the file extension that file extension is only spoken by nodejs as replacement for a flag that indicates esm usage.

the browser does not care for a extension it uses the MIME/TYPE information of the header.

even .php is a javascript file for the browser with the right header.

exse2 commented 3 years ago

frank-dspeed: Actually, it just does what you tell it to, no mime magic involved. If you want a module, you have to load it with "<script type="module"..".

frank-dspeed commented 3 years ago

@exse2 that is not correct you have forgotten about the CSP in the modern current browsers google CSP without that header you get nothing to work else it is a bug that will get fixed and it will not work.

but that is such a big security loop hole that it will not work anyway so we are save and i am correct.

no script tag no matter if module or not will work without mime type text/javascript

exse2 commented 3 years ago

"To enable CSP, you need to configure your web server to return the Content-Security-Policy HTTP header." I've looked at a standard configured apache server and it did not yield that. Anyways, I don't know why we you are talking about some header nobody uses anyways and/or how JavaScript Modules are loaded in the browser all of a sudden when the actual problem is this editor just being unable to properly work with .mjs files.

I am unsubscribing from this list. Peace.

frank-dspeed commented 3 years ago

@exse2 i am talking about that because it is something that the browser enforces it will not load script with the wrong mime type i hope you got the point that .mjs is nothing you want to use and should switch to package type module if your using nodejs as target environment for nodejs as nodejs anyways always works with packages this is the right way to go

a package is a folder with a package.json so that indicates for all files in the folder the script type if you need to mix types you need to put them in seperated folders. you can then import them without extension.

and if you produce something for nodejs then again use package.json and ship .js files.

hope that makes sense to you and if you find maintainers that adopt that .mjs pattern then simply tell them about the fact that this is wrong.

for tooling authors also make sure that they only can indicate if a file is ESM via checking for the export or import keyword import() is valid in both cases so only the import and export keyword matter as require can be also done in both contexts.

as a general rule for tooling always check for 'import ' || 'export ') if that is inside the file it is 100% ESM if the extension is .js

igorskyflyer commented 3 years ago

Talking about browsers first, then Node.

Taken from v8.dev:

You may have noticed we’re using the .mjs file extension for modules. On the Web, the file extension doesn’t really matter, as long as the file is served with the JavaScript MIME type text/javascript. The browser knows it’s a module because of the type attribute on the script element.

Still, we recommend using the .mjs extension for modules, for two reasons:

During development, the .mjs extension makes it crystal clear to you and anyone else looking at your project that the file is a module as opposed to a classic script. (It’s not always possible to tell just by looking at the code.) As mentioned before, modules are treated differently than classic scripts, so the difference is hugely important! It ensures that your file is parsed as a module by runtimes such as Node.js and d8, and build tools such as Babel. While these environments and tools each have proprietary ways via configuration to interpret files with other extensions as modules, the .mjs extension is the cross-compatible way to ensure that files are treated as modules.

About Node, nowadays developers (including myself) are making hybrid modules (until ES modules are a standard in both Node and browsers), npm packages that include both a CommonJS module (for compatibility) and an ES module. If you set your module's type to module and ship .js files for both CommonJS and ES modules, you'll get... a completely broken package and on top of that's not the point here, anyway.

We are talking about Visual Studio Code (because TypeScript server doesn't support it) shows an error about .mjs files not being found when imported since it only supports .js files. Note that an error will appear in Visual Studio Code but the script will actually execute properly, so it's only development-time issue. Also, tsc can't work with .mjs files, neither input nor output.

frank-dspeed commented 3 years ago

@igorskyflyer this is the opinion of jake and surma at present that is total ok they act out of a diffrent view i think if they would know what we know as tooling authors they would agree with that. .mjs was a failure.

i my self was long time advocating for .mjs because it is really easy to see what is esm and what is not but at the end that does not matter because that .mjs files tell us not what they import and if that will be ESM.

and do not forget i even did invest time into patching typescript and experimenting with .mjs support

Image of Working Installation

sure .mjs and .cjs support would be possible even wihtout problem as input files i do agree with microsoft that typescript should not adopt nodejs filename conventions.

in some years everything will be .js and ESM and cjs will not get used as often when it gets used it will use dynamic import() end of story.

igorskyflyer commented 3 years ago

@frank-dspeed, yes, I guess we are all expecting that - a standard that works seamlessly because, before we had callback hell (we still do actually 😅) and now we have module hell. 😥 I have been working the past 3 days on some workaround for this issue that will enable developers to write their codebase once and distribute it as a valid CommonJS and ES modules while also handling the issue stated here.

frank-dspeed commented 3 years ago

@igorskyflyer please ship only esm with .js extension and package type module in the package.json that will help everyone every today supported nodejs version supports import() do not go into module hell please

igorskyflyer commented 3 years ago

@frank-dspeed, that's a no - we do that and we drop support for developers who (still) use CommonJS syntax/modules, I don't want to do that, my intention is quite the opposite - until we have a working (one) standard. Since you said to use import() for importing modules I assume you mean to use import() to dynamically consume ES modules in CommonJS which is possible but brings a whole set of problems. Examples provided by MDN Web Docs:

import('/modules/my-module.js')
  .then((module) => {
    // Do something with the module.
  });

or with syntactic sugar

let module = await import('/modules/my-module.js');

Either way, you either get an ugly callback hell - mentioned above - or you have to (a)wait for a Promise to resolve and use await for that which in turn brings other issues, e.g.:

Top-level 'await' expressions are only allowed when the 'module' option is set to 'esnext' or 'system', and the 'target' option is set to 'es2017' or higher.

Also, “Use dynamic import only when necessary. The static form is preferable for loading initial dependencies, and can benefit more readily from static analysis tools and tree shaking.” - again from MDN Web Docs which means it defeats the whole purpose of using ES modules in the first place and disables/limits tree-shaking thus basically leaving us with the fact that then ES modules behave the same way as CommonJS modules with a changed syntax.

frank-dspeed commented 3 years ago

Interristing Opinions here as i am rollup contributor i do not care for my self i specialised in forking importent cjs deps and transpil them to clean esm so you will never hurt me i only wanted to make sure that we get a better core ecosystem and that has 100% nothing to do with the require syntax.

you should be familar with the fact that tree shaking got invented by rollup.

but releeasing a lot of diffrent packaged code does not make the code better.

as also remember only a CJS user is forced to use dynamic Import and a browser user most of the time also uses dynamic import to lazyLoad that is total fine.

frank-dspeed commented 3 years ago

@igorskyflyer https://github.com/mdn/content/compare/main...frank-dspeed:patch-1 i will patch the MDN Article thanks for your input. it Helps us to identify Areas where the information is not as clear as it should be

igorskyflyer commented 3 years ago

@frank-dspeed, I agree, I am for that too but we are not there, yet. This is my opinion during this transition period until there is one standard in both Node and browsers. Also, the opinions I stated above are based purely on (available) facts and personal experience, I never said it makes the code better in any way, more compatible - yes.

Sidenote, this issue is about .mjs importing issue during development in Visual Studio Code but .cjs doesn't work as well.

frank-dspeed commented 3 years ago

@igorskyflyer thats true without patching typescript there is no chance and the typescript philosophie is against that you need to wait for alternativ resolve algo support. maybe it already even works i am not so deep into that.

but there are internals to allow diffrent resolve strategies.

for example import maps maybe already work and package,json works already

or adding // @ts-check on top of the file also works

petamoriken commented 3 years ago

FYI: https://datatracker.ietf.org/doc/html/draft-ietf-dispatch-javascript-mjs-09

frank-dspeed commented 3 years ago

@petamoriken can you please explain more deep why this is related to the deprecation of existing mime types ?

petamoriken commented 3 years ago

@frank-dspeed This draft also includes the adoption of .mjs as the new file extension for JavaScript 🙂

It is not possible to fully determine if a Source Text of ECMAScript is meant to be parsed in the Module or Script grammar goals based upon content alone. Therefore, scripting environments MUST use out of band information in order to determine what goal a Source Text should be treated as. To this end some scripting environments have chosen to adopt the new file extension of .mjs for this purpose.

https://datatracker.ietf.org/doc/html/draft-ietf-dispatch-javascript-mjs-09#section-3

frank-dspeed commented 3 years ago

@petamoriken i only read SOME (NodeJS) scripting environments have chosen ......

The Browser will stay with mime headers There is 100% sure zero effort in changing that. The Script Parsing type inside the browser gets declared via the script type the headers will stay the same.

NodeJS is the only driver for .mjs

Out of Browser view

Example

to be more clear this will never work with esm modules!

<script src="/my.mjs"></script>

and this will always work

<script src="/my.mjsOrNoMatterwhat" type="module"></script>

Out of Typescript view

Out of NodeJS view

by the way the only none well speced implementation that supports a lot of mixUps

Out of Embeddeder view

Embedders are projects that are using v8 or other popular engines directly

igorskyflyer commented 3 years ago

@frank-dspeed, just to be clear, this:

<script src="/myModule.mjs"></script>

was never proposed nor supposed to work - as far as I am aware of. The attribute type with a value of module is mandatory for a given JavaScript file to be parsed as a module. Thus, the final code should look like:

<script src="/myModule.mjs" type="module"></script>

Also, .mjs files/scripts work perfectly fine after defining their MIME type on the server, tested on XAMPP.

frank-dspeed commented 3 years ago

@igorskyflyer i did list all requirements including the mime header and in my example i did .mjsOrAny to demonstrate that it has nothing to do with the extension. and your correct without type it is not suposed to work you maybe did not read the whole story.

igorskyflyer commented 3 years ago

@frank-dspeed, my apologies then, I skim-read the comment. Either way, it is evident that we are still waiting for a unique solution for this mjs/modules hell. 😭😅

frank-dspeed commented 3 years ago

@IgorNovozhilov There is no real module hell anymore the way forward is clear simply drop cjs usage call the files .js and configure typescript correct per folder.

Sure i agree it is a lot to know and do but it is not unsolved at all and .mjs remains a nodejs only topic.

Update for thumbs down

I want to offer some more detailed Information as there are still people who did not get it. You should create dev-bundels of your dependencies. isolate the packaging of your vendored dependencies from the Main Application that your creating. This will lead to consistent dependencies.

also this got now fixed via a new moduleResolution algo called "node12" as this is a node only thing