ES6 imports breaking client in Expo
What are you generating using Kiota, clients or plugins?
API Client/SDK
In what context or format are you using Kiota?
Nuget tool
Client library/SDK language
TypeScript
Describe the bug
Relative Imports now include a "index.js" suffix which seems to be causing issues when bundling in React native/expo.
Possible related issue #4950
Imports are currently generated as
// @ts-ignore
import { DocumentsRequestBuilderNavigationMetadata, type DocumentsRequestBuilder } from './documents/index.js';
// @ts-ignore
import { ProfilesRequestBuilderNavigationMetadata, ProfilesRequestBuilderRequestsMetadata, type ProfilesRequestBuilder } from './profiles/index.js';
// @ts-ignore
import { ThreadsRequestBuilderNavigationMetadata, type ThreadsRequestBuilder } from './threads/index.js';
Example error message by Expo
Android Bundling failed 63ms C:\Users\[REDACTED]\node_modules\expo-router\entry.js (1 module)
Unable to resolve "../../../models/index.js" from "lib\api\v1\auth\register\index.ts"
Expected behavior
Imports should be generated as
// @ts-ignore
import { DocumentsRequestBuilderNavigationMetadata, type DocumentsRequestBuilder } from './documents';
// @ts-ignore
import { ProfilesRequestBuilderNavigationMetadata, ProfilesRequestBuilderRequestsMetadata, type ProfilesRequestBuilder } from './profiles';
// @ts-ignore
import { ThreadsRequestBuilderNavigationMetadata, type ThreadsRequestBuilder } from './threads';
Or provide an additional flag to override the default behaviour.
How to reproduce
Generate any typescript client with kiota generate
Open API description file
No response
Kiota Version
1.18.0+5c6b5d0ef23865ba2f9d9f0b9fe4b944cf26b1ec
Latest Kiota version known to work for scenario above?(Not required)
1.15
Known Workarounds
- Search and replace to remove "/index.js".
- Downgrade to version 1.15
Configuration
No response
Debug output
Click to expand log
```</details>
### Other information
This is most likely due to #4815
Thanks for raising this @BrianUribe6
To confirm, any chance you've updated your project to use the config as outlined at this link? https://learn.microsoft.com/en-us/openapi/kiota/quickstarts/typescript#project-configuration
Yes, some of the settings are already defined in "expo/tsconfig.base", but to make sure I included them again. I'd like to re-emphasize that by completely removing "/index" from every import the client works as expected even with expo's default tsconfig.
tsconfig.json
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"paths": {
"@/*": ["./*"]
},
"lib": ["DOM", "ESNext", "ES2015"],
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "NodeNext",
"module": "NodeNext"
},
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"]
}
expo/tsconfig.base
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Expo",
"compilerOptions": {
"allowJs": true,
"esModuleInterop": true,
"jsx": "react-native",
"lib": ["DOM", "ESNext"],
"moduleResolution": "node",
"noEmit": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"target": "ESNext"
},
"exclude": ["node_modules", "babel.config.js", "metro.config.js", "jest.config.js"]
}
for anybody following along this conversation, a PR was started at #6173 but we still need more evidence this would be a positive change. Please join there.
We still need this, right now we are using this workaround: fix-generated-api-client.js
// eslint-disable-next-line @typescript-eslint/no-require-imports
const fs = require("fs");
// eslint-disable-next-line @typescript-eslint/no-require-imports
const path = require("path");
const directory = "./lib/api-client";
const search = "/index.js';";
const replace = "/index';";
function processDirectory(dir) {
const files = fs.readdirSync(dir);
files.forEach((file) => {
const fullPath = path.join(dir, file);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
processDirectory(fullPath);
} else if (stat.isFile()) {
const content = fs.readFileSync(fullPath, "utf-8");
if (content.includes(search)) {
const updatedContent = content.replace(
new RegExp(search, "g"),
replace
);
fs.writeFileSync(fullPath, updatedContent, "utf-8");
console.log(`Updated: ${fullPath}`);
}
}
});
}
processDirectory(directory);
console.log("Replacement complete.");
and these commands in package.json:
"download-openapi": "curl https://someurl/openapi/v1.json -o openapi.json",
"generate-client": "kiota generate -l typescript -d ./openapi.json -c ApiClient -o ./lib/api-client --clean-output && node fix-generated-api-client.js",
"update-client": "npm run download-openapi && npm run generate-client",
Hey everyone,
We had a PR opened at #6173 where I had left a bunch of questions which were never answered.
Here is the reason why I originally added index.js in the es6 module change
You cannot omit the file extension or the index.js file name. This behavior has been inherited by Node's ESM implementation, but it is not a part of the ECMAScript specification.
There's technically a segment of the population that might transpile but not bundle the end result. In which case, if the backend is not smart enough to map the request to index.js, things will break. The question then becomes: is that population bigger or smaller than the rest of the ecosystem which does NOT want index.js?
Also, since the path is valid, I'd argue that React native/expo should be able to handle it properly.
I think a couple of additional data points would help us make an educated decision here:
- are there other scenarios in which the index.js breaks imports?
- do react native and expo have issues about that? have we tried creating one?
- can we find authoritative guidance that'd contradict MDN?
Getting answers to those questions would help make a decision with a high degree of confidence here.
@baywet as it is Kiota may not work on any bundled front-end library/framework just because of a file extension
This is a barebones example where I just generated a client using the sample Pet Store OpenAPI Spec and tried instantiating it https://codesandbox.io/p/sandbox/y6m7v7
As you can see it is not even usable because of the imports.
Since the time I submitted this issue my team and I have moved to other generators which are more suitable for typescript such as openapi-typescript. I think Kiota should take advantage of the rich type system that TypeScript has instead of adding anything in the runtime.
Any news on this? It breaks with NextJs v16 out of the box as well. Since the tool produces .ts files why does it add .js as extensions anyway? Should it not use .ts consistently at least?