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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  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. VendureConfig,
  10. } from '@vendure/core';
  11. import {
  12. buildSchema,
  13. GraphQLList,
  14. GraphQLNonNull,
  15. GraphQLObjectType,
  16. GraphQLSchema,
  17. GraphQLType,
  18. isInputObjectType,
  19. isObjectType,
  20. } from 'graphql';
  21. import { Plugin } from 'vite';
  22. export interface SchemaInfo {
  23. types: {
  24. [typename: string]: {
  25. [fieldname: string]: readonly [
  26. type: string,
  27. nullable: boolean,
  28. list: boolean,
  29. isPaginatedList: boolean,
  30. ];
  31. };
  32. };
  33. }
  34. function getTypeInfo(type: GraphQLType) {
  35. let nullable = true;
  36. let list = false;
  37. let isPaginatedList = false;
  38. // Unwrap NonNull
  39. if (type instanceof GraphQLNonNull) {
  40. nullable = false;
  41. type = type.ofType;
  42. }
  43. // Unwrap List
  44. if (type instanceof GraphQLList) {
  45. list = true;
  46. type = type.ofType;
  47. }
  48. if (type instanceof GraphQLObjectType) {
  49. if (type.getInterfaces().some(i => i.name === 'PaginatedList')) {
  50. isPaginatedList = true;
  51. }
  52. }
  53. return [type.toString().replace(/!$/, ''), nullable, list, isPaginatedList] as const;
  54. }
  55. function generateSchemaInfo(schema: GraphQLSchema): SchemaInfo {
  56. const types = schema.getTypeMap();
  57. const result: SchemaInfo = { types: {} };
  58. Object.values(types).forEach(type => {
  59. if (isObjectType(type) || isInputObjectType(type)) {
  60. const fields = type.getFields();
  61. result.types[type.name] = {};
  62. Object.entries(fields).forEach(([fieldName, field]) => {
  63. result.types[type.name][fieldName] = getTypeInfo(field.type);
  64. });
  65. }
  66. });
  67. return result;
  68. }
  69. const virtualModuleId = 'virtual:admin-api-schema';
  70. let defaultSchema: GraphQLSchema;
  71. let schemaInfo: SchemaInfo;
  72. export async function adminApiSchemaPlugin(options: { config: VendureConfig }): Promise<Plugin> {
  73. resetConfig();
  74. await setConfig(options.config ?? {});
  75. if (!schemaInfo) {
  76. const runtimeConfig = await runPluginConfigurations(getConfig() as any);
  77. const typesLoader = new GraphQLTypesLoader();
  78. const finalSchema = await getFinalVendureSchema({
  79. config: runtimeConfig,
  80. typePaths: VENDURE_ADMIN_API_TYPE_PATHS,
  81. typesLoader,
  82. apiType: 'admin',
  83. output: 'sdl',
  84. });
  85. const safeSchema = buildSchema(finalSchema);
  86. schemaInfo = generateSchemaInfo(safeSchema);
  87. }
  88. return {
  89. name: 'vendure-admin-api-schema',
  90. resolveId(id, importer) {
  91. if (id === virtualModuleId) {
  92. return id;
  93. }
  94. },
  95. load(id) {
  96. if (id === virtualModuleId) {
  97. return `
  98. export const schemaInfo = ${JSON.stringify(schemaInfo)};
  99. `;
  100. }
  101. },
  102. };
  103. }