generate-graphql-types.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { spawn } from 'child_process';
  2. import * as fs from 'fs';
  3. import { execute, GraphQLSchema, IntrospectionQuery, IntrospectionSchema, parse } from 'graphql';
  4. import { makeExecutableSchema } from 'graphql-tools';
  5. import { buildClientSchema, introspectionQuery, printSchema } from 'graphql/utilities';
  6. import { fileLoader, mergeTypes } from 'merge-graphql-schemas';
  7. import { API_PATH, API_PORT } from '../shared/shared-constants';
  8. // tslint:disable:no-console
  9. const API_URL = `http://localhost:${API_PORT}/${API_PATH}`;
  10. const SCHEMA_JSON_FILE = '../schema.json';
  11. const CLIENT_SCHEMA_FILES = './src/app/data/types/**/*.graphql';
  12. const CLIENT_QUERY_FILES = '"./src/app/data/(queries|mutations|fragments)/**/*.ts"';
  13. const TYPESCRIPT_DEFINITIONS_FILE = './src/app/data/types/gql-generated-types.ts';
  14. main().catch(e => {
  15. console.log('Could not generate types!', e);
  16. process.exitCode = 1;
  17. });
  18. /**
  19. * This script uses apollo-codegen to generate TypeScript interfaces for all
  20. * GraphQL queries defined in the admin-ui app. Run it via the package.json
  21. * script "generate-gql-types".
  22. */
  23. async function main(): Promise<void> {
  24. const introspectionQueryFromApi = await downloadSchemaFromApi(API_URL);
  25. const combinedSchema = await combineSchemas(introspectionQueryFromApi, CLIENT_SCHEMA_FILES);
  26. fs.writeFileSync(SCHEMA_JSON_FILE, JSON.stringify(combinedSchema));
  27. console.log(`Generated schema file: ${SCHEMA_JSON_FILE}`);
  28. await generateTypeScriptTypesFromSchema(
  29. SCHEMA_JSON_FILE,
  30. CLIENT_QUERY_FILES,
  31. TYPESCRIPT_DEFINITIONS_FILE,
  32. );
  33. console.log('Generated TypeScript definitions!');
  34. }
  35. /**
  36. * Downloads the schema from the provided GraphQL endpoint using the `apollo schema:download`
  37. * cli command and returns the result as an IntrospectionQuery object.
  38. */
  39. async function downloadSchemaFromApi(apiEndpoint: string): Promise<IntrospectionQuery> {
  40. const TEMP_API_SCHEMA = '../schema.temp.json';
  41. await runCommand('yarn', ['apollo', 'schema:download', TEMP_API_SCHEMA, `--endpoint=${API_URL}`]);
  42. console.log(`Downloaded schema from ${API_URL}`);
  43. const schemaFromApi = fs.readFileSync(TEMP_API_SCHEMA, { encoding: 'utf8' });
  44. fs.unlinkSync(TEMP_API_SCHEMA);
  45. const introspectionSchema: IntrospectionSchema = JSON.parse(schemaFromApi);
  46. return {
  47. __schema: introspectionSchema,
  48. };
  49. }
  50. async function introspectionFromSchema(schema: GraphQLSchema): Promise<IntrospectionQuery> {
  51. const queryAST = parse(introspectionQuery);
  52. const result = await execute(schema, queryAST);
  53. return result.data as IntrospectionQuery;
  54. }
  55. /**
  56. * Combines the IntrospectionQuery from the GraphQL API with any client-side schemas as defined by the
  57. * clientSchemaFiles glob.
  58. */
  59. async function combineSchemas(
  60. introspectionQueryFromApi: IntrospectionQuery,
  61. clientSchemaFiles: string,
  62. ): Promise<IntrospectionQuery> {
  63. const schemaFromApi = buildClientSchema(introspectionQueryFromApi);
  64. const clientSchemas = fileLoader(clientSchemaFiles);
  65. const remoteSchema = printSchema(schemaFromApi);
  66. const typeDefs = mergeTypes([...clientSchemas, remoteSchema], {
  67. all: true,
  68. });
  69. const executableSchema = makeExecutableSchema({
  70. typeDefs,
  71. resolverValidationOptions: { requireResolversForResolveType: false },
  72. });
  73. const introspection = await introspectionFromSchema(executableSchema);
  74. return introspection;
  75. }
  76. /**
  77. * Generates TypeScript definitions from the provided schema json file sing the `apollo codegen:generate` cli command.
  78. */
  79. async function generateTypeScriptTypesFromSchema(
  80. schemaFile: string,
  81. queryFiles: string,
  82. outputFile: string,
  83. ): Promise<number> {
  84. return runCommand('yarn', [
  85. 'apollo',
  86. 'codegen:generate',
  87. outputFile,
  88. '--addTypename',
  89. `--queries=${queryFiles}`,
  90. `--schema ${schemaFile}`,
  91. ]);
  92. }
  93. /**
  94. * Runs a command-line command and resolves when completed.
  95. */
  96. function runCommand(command: string, args: string[]): Promise<number> {
  97. return new Promise((resolve, reject) => {
  98. const cp = spawn(command, args, { shell: true });
  99. cp.on('error', reject);
  100. cp.stdout.on('data', data => {
  101. if (4 < data.length) {
  102. console.log(`${data}`);
  103. }
  104. });
  105. cp.stderr.on('data', data => {
  106. if (4 < data.length) {
  107. console.log(`${data}`);
  108. }
  109. });
  110. cp.on('close', code => {
  111. if (code !== 0) {
  112. reject(code);
  113. }
  114. resolve(code);
  115. });
  116. });
  117. }