[FEAT] Support for .ts file extensions
Is your feature request related to a problem? Please describe.
In node.js 23 we have --experimental-strip-types now which allows us to run .ts files directly (with some limitations) and import .ts files directly.
With emitLegacyCommonJSImports set to true we get .js extension, but there is no configuration possible to get .ts extension.
Describe the solution you'd like
An alternative configuration options that would allow having .ts extension. It could be full-control like "customExtension" or just a simple enum of 3 (none, js, ts).
Describe alternatives you've considered As an alternative today we have to "pre-build" typescript before running node.js, which has worked fine since forever but would be cool if we could skip this step.
If there's a decision made by @eddeee888 on how this should look like (configuration options/possible values) I can take up the implementation and open a PR.
Hi @nickmessing , I think this is a great question for https://github.com/dotansimha/graphql-code-generator 🙂 Once we get it over the line there, we can integrate it here as well
Another issue is that the emitted code doesn't specify type imports, e.g.
import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql';
should be
import { type GraphQLResolveInfo, GraphQLScalarType, type GraphQLScalarTypeConfig } from 'graphql';
See erasable syntax only.
This script fixes the issues above as a temporary workaround. Might need to edit the paths.
#!/usr/bin/env node
/*
Script that fixes typescript errors in generated files, until this issue is solved:
https://github.com/eddeee888/graphql-code-generator-plugins/issues/364
*/
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
// Get the directory name of this script
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Path to the generated files
const typesPath = path.join(__dirname, "schema", "types.generated.ts");
const resolversPath = path.join(__dirname, "schema", "resolvers.generated.ts");
// Fix 1: Fix imports in types.generated.ts
try {
let typesContent = fs.readFileSync(typesPath, "utf8");
// Prefix all imports with "type"
// e.g.
// ```
// import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql';
// import { UserMapper } from './user/schema.mappers.js';
// import { GraphQLContext } from '../context/GraphQLContext.ts';
// ```
// should become
// ```
// import type { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql';
// import type { UserMapper } from './user/schema.mappers.js';
// import type { GraphQLContext } from '../context/GraphQLContext.ts';
// ```
// Step 1: Replace imports that don't already start with "type"
typesContent = typesContent.replace(
/import\s+{([^}]*)}\s+from\s+(['"].*['"])/g,
(match, importNames, source) => {
// Check if the import statement already has "type"
if (match.includes("import type")) {
return match;
}
// Add "type" to the import statement
return `import type {${importNames}} from ${source}`;
}
);
// Step 2: Fix duplicate "type" keywords in imports
typesContent = typesContent.replace(
/import\s+type\s+{([^}]*)}\s+from\s+(['"].*['"])/g,
(match, importNames, source) => {
// Remove individual "type" keywords from imports
const cleanedImports = importNames.replace(/\btype\s+/g, '');
// Return cleaned imports with a single "type" keyword at import level
return `import type { ${cleanedImports} } from ${source}`;
}
);
// Step 3: Fix .js extensions to .ts
typesContent = typesContent.replace(/\.js(['"])/g, '.ts$1');
fs.writeFileSync(typesPath, typesContent);
console.log("✅ Fixed types.generated.ts");
} catch (error) {
console.error("❌ Error fixing types.generated.ts:", error);
}
// Fix 2: Replace .js extensions with .ts in resolvers.generated.ts
try {
let resolversContent = fs.readFileSync(resolversPath, "utf8");
// Replace all .js extensions with .ts
resolversContent = resolversContent.replace(/\.js'/g, ".ts'");
fs.writeFileSync(resolversPath, resolversContent);
console.log("✅ Fixed resolvers.generated.ts");
} catch (error) {
console.error("❌ Error fixing resolvers.generated.ts:", error);
}
console.log("🎉 All GraphQL generated files fixed successfully!");
package.json
{
"scripts": {
"codegen": "graphql-codegen-esm && NODE_NO_WARNINGS=1 node ./src/graphql/fix-generated.ts",
}
}
codegen.ts
import type { CodegenConfig } from "@graphql-codegen/cli";
import { defineConfig } from "@eddeee888/gcg-typescript-resolver-files";
const config: CodegenConfig = {
schema: "src/graphql/schema/**/*.graphql",
emitLegacyCommonJSImports: false,
generates: {
"src/graphql/schema": defineConfig({
emitLegacyCommonJSImports: false,
typesPluginsConfig: {
contextType: "../context/GraphQLContext.ts#GraphQLContext",
},
}),
},
};
export default config;
@eddeee888 now that dotansimha/graphql-code-generator#10490 is released, should I pick this one up?
Yes please @nickmessing ! Thank you so much!