-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Provide a way to add the '.js' file extension to the end of module specifiers #16577
Comments
It's not just related to #13422, it's the same issue. But responses have been quite negatives, despite the fact I think it's a important issue, so hope your issue will be better received. |
Well, I hope its added, we were really looking forward to discussing my POC using this in my next TypeScript podcast, but looks like we will have to wait to use TypeScript with no build tools. |
At the moment TypeScript doesn't rewrite paths. It's definitely annoying, but you can currently add the |
Thanks for the tip, I'll write a shell/node script to do this. |
@DanielRosenwasser would it make sense to collect the native ES6 module issues under a label? |
Also, to generalize this issue a bit, I don't think it's actually about adding a |
I've come across another issue which isn't really the domain of typescript but it's for my use case. I'm not sure how to handle node_modules. Normally webpack bundles them into the code via the ts-loader but obviously, this is not understood by the browser:
Adding a js extension here is meaningless. I guess there would have to be a path expansion by typescript or a url resolver running on the server. I appreciate its a rather niche case, but I think it would be a way TS could shine early in this area. Maybe it could be a plugin to the tsc compiler? |
For anyone coming to this and wants an interim solution I wrote a script to add a js file extension to import statements: "use strict";
const FileHound = require('filehound');
const fs = require('fs');
const path = require('path');
const files = FileHound.create()
.paths(__dirname + '/browserLoading')
.discard('node_modules')
.ext('js')
.find();
files.then((filePaths) => {
filePaths.forEach((filepath) => {
fs.readFile(filepath, 'utf8', (err, data) => {
if (!data.match(/import .* from/g)) {
return
}
let newData = data.replace(/(import .* from\s+['"])(.*)(?=['"])/g, '$1$2.js')
if (err) throw err;
console.log(`writing to ${filepath}`)
fs.writeFile(filepath, newData, function (err) {
if (err) {
throw err;
}
console.log('complete');
});
})
})
}); I might make this into a cli tool.. |
@justinfagnani's comment hits the nail on the head.
when you write import { KeyCodes } from 'vanilla-typescript'; or for that matter import { KeyCodes } from 'vanilla-javascript'; you are importing from an module specifier, it may or may not be a file but adding If you are writing a NodeJS application then the NodeJS Require algorithm will attempt various resolutions but it will likely not attempt to resolve it to Other environments, such as AMD have differences as to how they perform this resolution but one thing that all of these environments have in common is some notion of an abstracted module specifier. I bring this up because the ES Module implementations shipping in various browsers implement something that is incomplete. If we consider even our simplest dependencies, and as soon as we broach the subject of transitive ones, it becomes clear that there will need to be a way to configure the doggone thing. That may be far off, but as you are discovering, it is not realistic to write to this (politely) proof of concept implementation we have been given. Furthermore, I do not see how TypeScript could possibly help here since the issue is environment specific. @quantuminformation your program for adding I am basically just ranting about the fact that the implementation of ES Modules that was released is tremendously far from adequate. Again NodeJS, RequireJS AMD, Dojo AMD, Sea Package Manager, CommonJS, Browserify, Webpack, SystemJS, all have their own differing ways of doing things but they all provide abstract name resolution. They have to provide it because it is fundamental. Thank you for reading my rant. |
Not sure which version of TS added it, but imports such as ' |
It's possible since TS 2.0. But tools like webpack don't support it so in the end it's useless. |
It's useless if one is using ts-loader on sources (the most common use-case). I wonder if I could implement a quick transformation in |
Feel free to do so, that would be great. I posted the issue on main webpack ts loaders, like ts-loader, a few months ago, and I've been quite badly received... For information, there is no problem with the rollup typescript plugin, as a proof it's doable. |
I fail to see what good this does until browser loader implementations and the WGATWG loader spec support at least some configuration because most dependencies won't load correctly. From my point of view, none of this matters until it is practical to use the native loader on an import that refers to an arbitrary string literal specifier, something that may not yet be a URL, and have that go through a transformation that yields the actual URL. Until then we will remain dependent on tools like SystemJS and Webpack. |
I created a tiny transformer that strips the '.js' from import/export statements. https://gist.github.com/AviVahl/40e031bd72c7264890f349020d04130a Using this, one can bundle ts files that contain imports from files that end with There is probably a limited number of use-cases where this is useful. EDIT: I updated the gist to work with exports as well. it's naive and not optimized, but works. |
Any movement on this issue? |
This matter of extension take us back to the very begining of TypeScript and why a Since that matter of
|
Hello, I'm coming at this late but would like to help. I am having trouble understanding what the issue is here. From the OP example it is: import { ModalBackground } from './ModalBackground'; Is the issue that we don't know what If we run |
This issue is also something the RxJS community is very interested in. What is the timeline on a solution for this? Is it even prioritized? Are there any third party transformations that would help? |
I'm not really sure if this is a problem if the output target is ES2015 is it? This could maybe fall into the domain of a ES2015browser capability. Even more so, @justinfagnani can't we push for this as a platform goal to worry about? (Maybe need to fork into separate thread). |
In Webpack 5+, this is required to load Svelte correctly, due to what appears to be code generation issues with TypeScript: microsoft/TypeScript#16577 This works around that by using telling end users to use a newly added feature in Webpack 5+, that uses Webpack 4 behavior for resolving module names: https://webpack.js.org/configuration/module/#resolvefullyspecified
In Webpack 5+, this is required to load Svelte correctly, due to what appears to be code generation issues with TypeScript: microsoft/TypeScript#16577 This works around that by using telling end users to use a newly added feature in Webpack 5+, that uses Webpack 4 behavior for resolving module names: https://webpack.js.org/configuration/module/#resolvefullyspecified
Jeez, this is super annoying. |
? It works fine example.ts import { Example } from './example.js'; compiles to example.js import { Example } from './example.js'; |
Yes, thanks you, and apologies, sort of. |
This same argument has been made before in this thread but it still doesn't make sense. You can not import something from a compiled file, especially one that hasn't been created yet. No other programming language allows that. Java doesn't allow you to |
example.ts
I'm writing in typescript; importing something from a local path in the same project that only contains typescript files. How does appending a .js to the import statement make sense to you? We're not writing any javascript and there's no javascript file in the folder being referenced? The .js file being referenced doesn't exist; and won't exist until after it's been transpiled to js, and when that happens in many cases it'll be stored somewhere else. Source code shouldn't be referring to it's own build artifacts. At the very least, if I'm going to reference importing from another typescript file by name, it should be:
I further agree with the complaint that I shouldn't have to put the extension at all; let me just refer to ./example, and the transpiler can find the relative .ts file. And the transpiler should be responsible for appending .js in the transpiled output, We really shouldn't have to provide the ".js" in the .ts source code. |
For the record, I totally agree with @db984 |
No, that's exactly what Java does. Exactly.
Which makes sense because C/C++ includes are done as text preprocessing on the source files. Whole different approach, whole different ballgame. Forget knowing about compiled files, If you're requiring the "file to exist", you should also have a problem with the "non-existent file" import * as _ from 'lodash' The answer is that TypeScript doesn't change module specifiers. You can use TS doesn't know anything about JS file<->module specifier relationships. It just knows how to resolve TS types from module specifiers. TypeScript plays around with extensions (.js, .ts, .jsx, .tsx, .d.ts) and directories ( If you import from
Then use a module loader that works with extensionless module specifiers. But if you're interested in a module loader (native ES modules) that requires the exact path of the JS modules, know that TypeScript in fact supports using the exact path of the JS module. (Said differently, TypeScript supports locating TS types using the exact path of the JS module.) |
@FunctionPoint as an alternative option, you can use a compiler plugin to add the extension at compile time: https://github.com/Zoltu/typescript-transformer-append-js-extension |
No it's not and you know exactly what I meant 🙂 What Java allows us to do is to import source files, not compiled files. It may so happen that in the compiled Java bytecode it does reference a file ending in @db984's reply is spot on. |
Where @pauldraper writes:
A bare module specifier is a completely separate case. Here we are clearly relying on the compiler/runtime and project configuration to help it find a module named "lodash". Perhaps the file being imported is in When I import from But when I import from To argue that It seems far more logical to me that typescript should explicitly support typescript source code esm modules that are part of the project and allow you to reference And again, I would go even further and also allow you to leave the extension off entirely, and it will look for the module specified at that path, much as it does for commonJS modules, even though that's not in the javascript esm standard. It's doing a transpilation step anyway, so it can do the legwork of resolving the the module specifier to a file reference. I'm using typescript because it makes life easier after all. |
I agree that in an ideal world, TypeScript could take total ownership of the import paths and remap them appropriately. But |
a) This already deviates from javascript; so 'not touching it at all' is already not accurate. b) It's been working fine for project relative imports in commonJS all this time, with very similar constraints so I'm really not sure why you think this is a significant challenge where support could only be "spotty at best". As near as I can tell the only real change is that esm module resolution standard requires the explcit .js extension when referencing importing a js file from a relative location where as the commonJS standard specified that it would search filename.js then filename.json etc automatically in this case. |
Well I've read all 385 comments again now as part of a FAQ update and here are my main takeaways:
Experience on these sorts of threads has shown that discussion tends to just remain circular at this point, so I'm going to lock to avoid notification noise for commentors and repo watchers. We are not going to implement features, even under a commandline flag, that imply changing JS semantics by rewriting it during emit. |
In order to use es6 modules in the browser, you need a .js file extension. However output doesn't add it.
In ts:
import { ModalBackground } from './ModalBackground';
In ES2015 output:
import { ModalBackground } from './ModalBackground';
Ideally I would like this to be output
import { ModalBackground } from './ModalBackground.js';
That way I can use the output in Chrome 51
Related to #13422
The text was updated successfully, but these errors were encountered: