vite-plugin-admin-api-schema.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import {
  2. GraphQLList,
  3. GraphQLNonNull,
  4. GraphQLObjectType,
  5. GraphQLSchema,
  6. GraphQLType,
  7. isEnumType,
  8. isInputObjectType,
  9. isObjectType,
  10. isScalarType,
  11. } from 'graphql';
  12. import { Plugin } from 'vite';
  13. import { generateSchema } from './utils/schema-generator.js';
  14. import { ConfigLoaderApi, getConfigLoaderApi } from './vite-plugin-config-loader.js';
  15. export type FieldInfoTuple = readonly [
  16. type: string,
  17. nullable: boolean,
  18. list: boolean,
  19. isPaginatedList: boolean,
  20. ];
  21. export interface SchemaInfo {
  22. types: {
  23. [typename: string]: {
  24. [fieldname: string]: FieldInfoTuple;
  25. };
  26. };
  27. inputs: {
  28. [typename: string]: {
  29. [fieldname: string]: FieldInfoTuple;
  30. };
  31. };
  32. scalars: string[];
  33. enums: {
  34. [typename: string]: string[];
  35. };
  36. }
  37. const virtualModuleId = 'virtual:admin-api-schema';
  38. const resolvedVirtualModuleId = `\0${virtualModuleId}`;
  39. export function adminApiSchemaPlugin(): Plugin {
  40. let configLoaderApi: ConfigLoaderApi;
  41. let schemaInfo: SchemaInfo;
  42. return {
  43. name: 'vendure:admin-api-schema',
  44. configResolved({ plugins }) {
  45. configLoaderApi = getConfigLoaderApi(plugins);
  46. },
  47. async buildStart() {
  48. const { vendureConfig } = await configLoaderApi.getVendureConfig();
  49. if (!schemaInfo) {
  50. const safeSchema = await generateSchema({ vendureConfig });
  51. schemaInfo = generateSchemaInfo(safeSchema);
  52. }
  53. },
  54. resolveId(id) {
  55. if (id === virtualModuleId) {
  56. return resolvedVirtualModuleId;
  57. }
  58. },
  59. load(id) {
  60. if (id === resolvedVirtualModuleId) {
  61. return `
  62. export const schemaInfo = ${JSON.stringify(schemaInfo)};
  63. `;
  64. }
  65. },
  66. };
  67. }
  68. function getTypeInfo(type: GraphQLType) {
  69. let nullable = true;
  70. let list = false;
  71. let isPaginatedList = false;
  72. // Unwrap NonNull
  73. if (type instanceof GraphQLNonNull) {
  74. nullable = false;
  75. type = type.ofType;
  76. }
  77. // Unwrap List
  78. if (type instanceof GraphQLList) {
  79. list = true;
  80. type = type.ofType;
  81. }
  82. if (type instanceof GraphQLObjectType) {
  83. if (type.getInterfaces().some(i => i.name === 'PaginatedList')) {
  84. isPaginatedList = true;
  85. }
  86. }
  87. return [type.toString().replace(/!$/, ''), nullable, list, isPaginatedList] as const;
  88. }
  89. function generateSchemaInfo(schema: GraphQLSchema): SchemaInfo {
  90. const types = schema.getTypeMap();
  91. const result: SchemaInfo = { types: {}, inputs: {}, scalars: [], enums: {} };
  92. Object.values(types).forEach(type => {
  93. if (isObjectType(type)) {
  94. const fields = type.getFields();
  95. result.types[type.name] = {};
  96. Object.entries(fields).forEach(([fieldName, field]) => {
  97. result.types[type.name][fieldName] = getTypeInfo(field.type);
  98. });
  99. }
  100. if (isInputObjectType(type)) {
  101. const fields = type.getFields();
  102. result.inputs[type.name] = {};
  103. Object.entries(fields).forEach(([fieldName, field]) => {
  104. result.inputs[type.name][fieldName] = getTypeInfo(field.type);
  105. });
  106. }
  107. if (isScalarType(type)) {
  108. result.scalars.push(type.name);
  109. }
  110. if (isEnumType(type)) {
  111. result.enums[type.name] = type.getValues().map(v => v.value);
  112. }
  113. });
  114. return result;
  115. }