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

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