ferdikoomen / openapi-typescript-codegen

NodeJS library that generates Typescript or Javascript clients based on the OpenAPI specification

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

A flag to determine file extensions in import statements

justin-calleja opened this issue Β· comments

Describe the solution you'd like

Through some flag to the cli - I'd like to go from generating imports like these:

import type { BaseHttpRequest } from './core/BaseHttpRequest';
import type { OpenAPIConfig } from './core/OpenAPI';

to imports like these:

import type { BaseHttpRequest } from './core/BaseHttpRequest.js';
import type { OpenAPIConfig } from './core/OpenAPI.js';

Typescript knows to look for a .ts file when compiling so even in .ts files, in import statements, it seems like the recommended approach is to use .js extension: microsoft/TypeScript#40878 (comment)

Adding the extension makes it more compliant to ESM spec it seems.

As far as TS is concerned, it's the dev's responsibility to specify this. This is ok for code I write... but code that is generated from a tool needs to support adding this.

To recap - I would like some flag / option in api - such that when set, import statements end with the .js file extension.

Why? This will allow me to directly run node against a .mjs file (or .js file with type of package set to module) - which imports a package X. X was generated via this cli. More specifically, JS files were generated from the generated TS files from this cli. Because the generated TS files do not have file extensions, I get errors trying to run node directly against my .js file because it imports js files which don't have the file extension in their imports (because they were generated from the TS files generated from this cli which did not have file extension).

@Ted-Barrett Thanks for the workaround, it solved my problem
I don't understand how its possbile to use import without file extension at all

@justin-calleja are you able to share more about the package X? How is it transformed into JavaScript from TypeScript?

@mrlubos - from what I can gather from my initial message ^ ... X is basically the output of this tool openapi-typescript-codegen. What I think was happening was that this tool generates TS files from openapi spec - an HTTP client.. that's the point of this tool. But, in so doing, it breaks ESM spec from what I can tell. Its generated import statements do not have a file extension which is required by the ESM standard and tools like ts-node --esm.

So how has this worked so far? From what I can tell, the output of this tool only works when running with node in CJS mode (its default mode). If you add a type: module to a package's package.json, you tell node to run in ESM mode meaning it will expect import / export statements... also, it needs the file extension - that's part of ESM from what I can tell.

Typescript is written in ESM, but it transpiles to CJS or ESM or both / whatever. Point is, what TS generates is what node actually runs. Now TS will not touch import statement file extensions. That's the dev's responsibility to know which file they are statically importing. A responsibility the dev does not have control over when the code is generated with this tool.

TS however, is not stupid. If you import a .js file from a TS file, when transpiling, TS is capable of figuring out you meant the TS equivalent of that file and has in-built file resolution capabilities similar to how node running in CJS mode has file resolution capabilities when using require. So using .js in TS files in import / export statements is fine.

Does this clarify things a bit? I cannot share package X no as it's generated from an internal API's swagger def... nor should it matter what exactly X is... it's the output of this tool given a swagger spec... any spec would do - there are many open sourced ones to copy pasta 🍝 πŸ™‚

Anyway, I ended up not needing this as my team stubbornly keeps using commonjs. Re this issue - I think all that's needed is to just add a .js to all generated TS file's import statments. TS can then be configured to output CJS or ESM. When generating ESM, TS won't touch your file extensions so you need to get it right... in this case, this tool needs to get it right. The CJS output - that should still keep working if you explicitly have the file extension in there.

@justin-calleja i see. Instead of manually supplying a flag, would it be possible to infer the desired output from your TypeScript config?

@mrlubos I think the desired output is always to have a .js extension in your TS file's import statements. That way, when TS generates JS - regardless of runtime having strict ESM requirements or not - it would still work. Node in CJS mode runs require statements with .js extensions and node in ESM mode must have them. That supports both. It you keep them out.. you limit the output to work with runtimes that are more flexible but not ones running with strict ESM semantics.

i.e. I mentioned a cli flag... but I don't really see why you'd want to have an import statement in your TS without a file extension. In this tools case, that file extension is always JS as we're not importing other assets here and delegating to a bundler to sort those out.

If you consult the package's tsconfig.json and see a "module": "commonjs"... it still doesn't make sense to output TS files (from openapi.json / yaml) that have no file extension. Again, TS will not touch file extensions when generating JS code. If that JS code is in ESM format, not having a file extension means trouble for runtimes that strictly run according to the ESM spec.

Edit: that said, I am not aware of all cases and there might be some requirement to actually generate the client with TS files that have e.g. .mjs or .cjs in their import statements.. I think that's why I had opened this ticket with a flag in mind... as the import file extension is the dev's responsibility (they know what runtime they're using), this tool should allow them to generate the right TS for their use case rather than having to add more tools to edit the output after the generation - as suggested by @Ted-Barrett above. Thanks for that btw πŸ™‚

@justin-calleja the vast majority of TypeScript users and code is written without using file extensions when importing TS/JS modules. For those people, changing this behaviour would not adhere to their standards. We need to ideally find a way to detect the correct pattern from their project configuration. If we agree on how to detect that, I'd make that change

@mrlubos this is not an issue for me anymore. I did encounter this issue in a package that defined an API spec with a combination of zod and @asteasolutions/zod-to-openapi to generate the open api spec with... while at the same time, it also generated a TS client from that spec using this package.

Not sure if the issue was ts-node specific but the package in question was using .js file extensions in import statements and probably having a mix with the API spec part + TS generated code gave issues.

Anyway, that package was changed to output commonjs and I think that's how it was solved. Another solution can be to split out the package - one to be just api spec definition (+ schema generation) - the other being a client generator that depends on the api spec def pkg.

Anyway - point is - there's ways around this. I would still argue that you don't know what issues a user may encounter and it makes sense to give them the opportunity to change the generated output... tsc is full of options to support many cases - but it seems like it does not offer an option to change suffixes in its output as it leaves that up to the user. But when using this tool, the user doesn't have a choice re what suffixes the import statements use. I would not consider that to be this tool's responsibility to figure out but instead, to provide the flexibility to the user.

Anyway, rant off πŸ™‚ I may or may not be right after all and it's not a big deal to add this in a fork if it's really a blocker after all.

Haha all good @justin-calleja, glad to hear that it's not a blocker for you anymore at least. If more people run into this problem I'd dedicate time to it, right now it doesn't seem to be a major issue

Agreed πŸ™‚ closing issue. Thanks for reading my verbose text πŸ˜…

@justin-calleja no problem, that context will be valuable to anyone who stumbles upon this issue in the future