schema.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import { cancel, intro, isCancel, log, outro, select, text } from '@clack/prompts';
  2. import pc from 'picocolors';
  3. import { withInteractiveTimeout } from '../../utilities/utils';
  4. const cancelledMessage = 'Schema generation cancelled.';
  5. export interface SchemaOptions {
  6. api: 'admin' | 'shop';
  7. format?: 'sdl' | 'json';
  8. fileName?: string;
  9. outputDir?: string;
  10. /** Specify the path to a custom Vendure config file */
  11. config?: string;
  12. }
  13. /**
  14. * This command is used to generate a schema file for use with other GraphQL tools
  15. * such as IDE plugins.
  16. */
  17. export async function schemaCommand(options?: SchemaOptions) {
  18. // Check if any non-interactive options are provided
  19. if (options?.api) {
  20. // Non-interactive mode
  21. await handleNonInteractiveMode(options);
  22. return;
  23. }
  24. // Interactive mode (original behavior)
  25. await handleInteractiveMode(options?.config);
  26. }
  27. async function handleNonInteractiveMode(options: SchemaOptions) {
  28. try {
  29. process.env.VENDURE_RUNNING_IN_CLI = 'true';
  30. const { generateSchema } = await import('./generate-schema/generate-schema');
  31. await generateSchema(options);
  32. process.env.VENDURE_RUNNING_IN_CLI = undefined;
  33. } catch (e: any) {
  34. log.error(e.message as string);
  35. if (e.stack) {
  36. log.error(e.stack);
  37. }
  38. process.exit(1);
  39. }
  40. }
  41. async function handleInteractiveMode(configFile?: string) {
  42. // eslint-disable-next-line no-console
  43. console.log(`\n`);
  44. intro(pc.blue('🛠️️ Generate a schema file of your GraphQL API'));
  45. const apiType: 'admin' | 'shop' | symbol = await withInteractiveTimeout(async () => {
  46. return await select({
  47. message: 'Which API should we target?',
  48. options: [
  49. { value: 'admin', label: 'Admin API' },
  50. { value: 'shop', label: 'Shop API' },
  51. ],
  52. });
  53. });
  54. if (isCancel(apiType)) {
  55. cancel(cancelledMessage);
  56. process.exit(0);
  57. }
  58. const format: 'sdl' | 'json' | symbol = await withInteractiveTimeout(async () => {
  59. return await select({
  60. message: 'What format should we use for the schema?',
  61. options: [
  62. { value: 'sdl', label: 'SDL format (default)' },
  63. { value: 'json', label: 'JSON introspection query result' },
  64. ],
  65. });
  66. });
  67. if (isCancel(format)) {
  68. cancel(cancelledMessage);
  69. process.exit(0);
  70. }
  71. const outputDir = await withInteractiveTimeout(async () => {
  72. return await text({
  73. message: 'Output directory:',
  74. initialValue: process.cwd(),
  75. });
  76. });
  77. if (isCancel(outputDir)) {
  78. cancel(cancelledMessage);
  79. process.exit(0);
  80. }
  81. const fileName = await withInteractiveTimeout(async () => {
  82. const defaultBase = `schema${apiType === 'shop' ? '-shop' : ''}`;
  83. return await text({
  84. message: 'File name:',
  85. initialValue: format === 'sdl' ? `${defaultBase}.graphql` : `${defaultBase}.json`,
  86. });
  87. });
  88. if (isCancel(fileName)) {
  89. cancel(cancelledMessage);
  90. process.exit(0);
  91. }
  92. try {
  93. process.env.VENDURE_RUNNING_IN_CLI = 'true';
  94. const { generateSchema } = await import('./generate-schema/generate-schema');
  95. await generateSchema({
  96. api: apiType,
  97. format,
  98. fileName,
  99. outputDir,
  100. config: configFile,
  101. });
  102. outro('✅ Done!');
  103. process.env.VENDURE_RUNNING_IN_CLI = undefined;
  104. } catch (e: any) {
  105. log.error(e.message as string);
  106. if (e.stack) {
  107. log.error(e.stack);
  108. }
  109. }
  110. }