-
Notifications
You must be signed in to change notification settings - Fork 191
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
File Extension Substitution for TypeScript Support #413
Comments
Hello, sorry for delay, honestly, I'm not sure that we should work like this, I see what you want to solve, the specification is quite clear about what you are asking cannot be done. If you want to expose tsx you should use condition names, so if you have |
Hi @alexander-akait . Thanks for the response. TypeScript's official module resolution works that way though (see Extension Substitution in their official docs). If it isn't supported by enhanced-resolve, it would be necessary to make an identical package that mirrors how TS does resolution. TS's module resolution directly mirrors that of Node.js, it only deviates when a path has been found in package.json. Is it still out of scope? When you say that the specification does not support my request, is it because the enhanced-resolve package only should implement Node.js resolution, without additional options? |
Please read #355, because we can't get files which were not exported, it is fully invalid (in my opinion) and literally breaks the meaning of the I am ready for dialogue on this issue once again, I would like to hear the developers' opinions here of typescript and Node.js /cc @andrewbranch Perhaps you can ping here the person responsible for this or clarify the logic |
I understand, but in my view Extension Substitution does not equal exporting files that the developer did not mean to export. foo.ts is just the TS version of foo.js. The file name is identical. Indeed, @andrewbranch would probably be the right person to provide color here... |
Yeah, I fully undestand you, but as before I said this is not entirely true from a specification point of view, so yes, I would like to hear @andrewbranch here and how they implemented this. As you can see you need to use the
Technically a library can export any files and if we think correctly, we should do this under different conditional names... But let's dicussion about it with ts team, I am fine to change logic to be align with them, just want that we look at solving the problem in the same way |
I’d like to understand what real-world scenario @magnusriga is trying to achieve, before I answer the theoretical design questions. |
Hi @andrewbranch, thanks for chiming in. My question stems from the eslint-import-resolver-typescript, which relies on enhanced-resolve. It is not possible for that ESLint plugin to mirror Typescript's resolution, without changes to enhanced-resolve itself (enabling extension substitution). So, if the package.json exports field maps to .js files (on the right hand side of the exports map), as is recommended in the TS docs (the output files should be .js(x), not .ts(x)), then extension substitution must take place for the imports to be found. I think the eslint-import-resolver-typescript is used by enough organizations and people to warrant attention here. E.g. vercel's style-guide. |
Is this just a webpack-bundled app, though? The reason to point imports/exports to output files is if you’re creating output files with tsc and running them in Node.js or another JavaScript runtime. If you’re bundling, or running TypeScript files directly, you can just point everything to the |
Indeed @andrewbranch , it is a monorepo with a component library and a few next.js apps, with both .js(x) and .ts(x) modules. Not sure if the components from the component library can be imported by .js(x) modules, if the library's exports points to .ts(x) files. Also, I was thinking of publishing the component library, and as such it would be nice if any app could consume it. What do you think? |
A few thoughts.
I think we’ve talked about this in another thread. I hadn’t thought much about it before that, but this seems like a recipe for pain when combined with package.json imports/exports wildcards. If the file extension isn’t consistent between all modules that match the wildcard pattern, you have to be ok with specifying the file extension in your import module specifier in the source code.
They should? Bundlers allow JS to import TS.
I’m still confused about the original request for Webpack to load input TypeScript files when the component library is being intentionally configured to have and resolve to output JavaScript files. You’re doing that so that any app can load it, but you don’t want to load it that way in your own app? Why should your app resolve to the input TypeScript files while others who install your library resolve to the output JavaScript files? You can have it both ways if you want, using custom conditions: {
"name": "component-library",
"exports": {
"./Button": {
"ts-source": "./src/Button.tsx",
"default": "./dist/Button.js"
}
}
} You could set the File extension substitution is an implementation detail for how TypeScript is able to operate on projects that are written with import paths and package.jsons that are valid for JavaScript runtimes. It’s not a feature that anyone else should copy. I think it would actually undermine TypeScript’s ability to model what it needs to if runtimes and bundlers copied its behavior. |
@andrewbranch thank you. I did not intend for myself to import it differently than other consumers. I am ok with all consumers, including myself, to import the components as compiled It seems, however, based on your answer, that it is indeed perfectly OK to use If I understood you correctly, the components can also be published as Lastly, based on your answer, I think |
I don't mind if we introduce your behavior as an option that will be disabled by default, and describe in the documentation that this is a feature for tooling only and should not be used as part of a normal setup, I understand that duplicating logic and rewriting the same thing adds problems to all of us |
And I'm glad to support other projects that use our library because it allows us to make it more stable and find more problems/bugs |
Great, @alexander-akait , sounds like an excellent approach. I am sure all users of that ESLint extension will be thankful for that addition (it is a relatively popular package). |
@magnusriga The only one - I'm afraid that it will be difficult for me to find time to implement this at this moment, there are a lot of tasks for this month, but I will be happy to accept a PR, so PR welcome ⭐ |
I’m curious why |
I have no idea, @andrewbranch . @JounQin might know. I just peaked at the source and saw they rely on |
The same problem for |
As an aside, for a quick fix, you can downgrade to // package.json
"imports": {
"#*": [
"./src/*.ts",
"./src/*.tsx"
]
},
"pnpm": {
"overrides": {
"eslint-import-resolver-typescript>enhanced-resolve": "5.17.0"
}
}, I tried working around it with different resolver options for @latest with custom conditions, aliases, and extension aliases and couldn't figure a better way around it. EDIT: I believe I have discovered the issue. Suppose your Webpack configuration has the following extensions: Then, suppose your imports/exports looks like If you update imports/exports to IMO, the best workaround if you are using TypeScript and a module bundler is to do this: // tsconfig.json
{
"compilerOptions": {
"customConditions": ["@repo/development"],
},
}
// package.json
{
"imports": {
"#*": {
"@repo/development": "./src/*.js",
"default": "./src/*"
}
},
} So TypeScript can do its file extension substitution correctly, and extensionless paths remain as-is for use by Webpack/module bundlers. This works well for my use case of Next.js (uses Next.js internally), ESLint ( |
@jeremy-code This problem has nothing to do with what you mentioned, this is a request for improvement |
I am trying to get the enhanced resolver to work with a TypeScript self-reference, both via package.json
exports
andimports
.When TypeScript looks up modules it deviates from the Node resolver once a path has been found. Specifically, it conducts what is called File Extension Substitution where it takes the path it found from
package.json
and swaps out the extension with.ts
,.tsx
,.d.ts
,.js
. and.jsx
, in that order.It seems enhanced resolver does not do that kind of substitution, is that right? Any way to make it mimic TypeScript's resolution algorithm?
Note: The import resolution works when we change the extensions to .tsx in the
exports
andimports
, but the idea is that the resolver should do extension substitution when it looks for the TS files.Minimal repo: https://github.com/magnusriga/my-app
Code excerpts follow below.
Importing file:
Local package.json
The text was updated successfully, but these errors were encountered: