graphql-code-generator-plugins icon indicating copy to clipboard operation
graphql-code-generator-plugins copied to clipboard

[FEAT] Support for .ts file extensions

Open nickmessing opened this issue 10 months ago • 6 comments

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.

nickmessing avatar Feb 27 '25 07:02 nickmessing

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.

nickmessing avatar Feb 27 '25 07:02 nickmessing

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

eddeee888 avatar Feb 27 '25 09:02 eddeee888

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.

olalonde avatar Apr 25 '25 00:04 olalonde

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;

olalonde avatar Apr 25 '25 00:04 olalonde

@eddeee888 now that dotansimha/graphql-code-generator#10490 is released, should I pick this one up?

nickmessing avatar Nov 20 '25 14:11 nickmessing

Yes please @nickmessing ! Thank you so much!

eddeee888 avatar Nov 25 '25 14:11 eddeee888