diff --git a/packages/core/src/di/inject-many.ts b/packages/core/src/di/inject-many.ts deleted file mode 100644 index 5766608040..0000000000 --- a/packages/core/src/di/inject-many.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { ServiceIdentifier } from './types'; - -import { DESIGN_PARAM_TYPES } from './utils'; - -declare var Reflect: any; - -export function InjectMany(serviceIdentifiers: Array>) { - return (target: any, _targetKey: any, index: number) => { - let dependencies = Reflect.getMetadata(DESIGN_PARAM_TYPES, target) || []; - if (!dependencies) { - throw new Error('You must decorate the provider class with @Injectable()'); - } - if (typeof index === 'number') { - dependencies[index] = serviceIdentifiers; - } else { - dependencies = serviceIdentifiers; - } - Reflect.defineMetadata(DESIGN_PARAM_TYPES, dependencies, target); - return target; - }; -} diff --git a/packages/core/src/graphql-module.ts b/packages/core/src/graphql-module.ts index 54ff6ac8e4..bd877dc51e 100644 --- a/packages/core/src/graphql-module.ts +++ b/packages/core/src/graphql-module.ts @@ -122,9 +122,9 @@ export class GraphQLModule { constructor( private _options: GraphQLModuleOptions = {}, private _moduleConfig: Config = {} as Config, - ) { - _options.name = _options.name || Math.floor(Math.random() * Math.floor(Number.MAX_SAFE_INTEGER)).toString(); - } + ) { + _options.name = _options.name || Math.floor(Math.random() * Math.floor(Number.MAX_SAFE_INTEGER)).toString(); + } /** * Creates another instance of the module using a configuration @@ -335,7 +335,7 @@ export class GraphQLModule { importsTypeDefs.add(typeDef); } } else { - importsTypeDefs.add(typeDefs); + importsTypeDefs.add(typeDefs); } } importsContextBuilders.add(contextBuilder); @@ -423,7 +423,7 @@ export class GraphQLModule { this._cache.contextBuilder = async networkRequest => { const importsContextArr$ = [...importsContextBuilders].map(contextBuilder => contextBuilder(networkRequest)); const importsContextArr = await Promise.all(importsContextArr$); - const importsContext = deepmerge.all(importsContextArr as any[]) as any; + const importsContext = importsContextArr.reduce((acc, curr) => ({ ...acc, ...curr as any }), {}); const moduleContext = await (this._options.contextBuilder ? this._options.contextBuilder(networkRequest, importsContext, this._cache.injector) : async () => ({})); const builtResult = { ...importsContext, @@ -462,184 +462,179 @@ export class GraphQLModule { * * @param request - the network request from `connect`, `express`, etc... */ - context = async (request: Request): Promise> => { - const moduleContext = await this.contextBuilder(request); - return { - ...moduleContext as any, - }; - } + context = async (request: Request) => this.contextBuilder(request) as Promise>; - get modulesMap() { - if (!this._cache.modulesMap) { - let modulesMap = this.createInitialModulesMap(); - modulesMap = this.checkAndFixModulesMap(modulesMap); - this._cache.modulesMap = modulesMap; - } - return this._cache.modulesMap; + get modulesMap() { + if (!this._cache.modulesMap) { + let modulesMap = this.createInitialModulesMap(); + modulesMap = this.checkAndFixModulesMap(modulesMap); + this._cache.modulesMap = modulesMap; } + return this._cache.modulesMap; + } - private createInitialModulesMap() { - const modulesMap = new Map>(); - const visitModule = (module: GraphQLModule) => { - if (!modulesMap.has(module.name)) { - modulesMap.set(module.name, module); - for (const subModule of module.selfImports) { - if (!subModule) { - throw new DependencyModuleUndefinedError(module.name); - } - if (typeof subModule !== 'string') { - visitModule(subModule); - } + private createInitialModulesMap() { + const modulesMap = new Map>(); + const visitModule = (module: GraphQLModule) => { + if (!modulesMap.has(module.name)) { + modulesMap.set(module.name, module); + for (const subModule of module.selfImports) { + if (!subModule) { + throw new DependencyModuleUndefinedError(module.name); + } + if (typeof subModule !== 'string') { + visitModule(subModule); } } - }; - visitModule(this); - return modulesMap; - } + } + }; + visitModule(this); + return modulesMap; + } - private checkAndFixModulesMap(modulesMap: ModulesMap): Map> { - const graph = new DepGraph>(); + private checkAndFixModulesMap(modulesMap: ModulesMap): Map> { + const graph = new DepGraph>(); - modulesMap.forEach(module => { - const moduleName = module.name; - if (!graph.hasNode(moduleName)) { - graph.addNode(moduleName); - } - }); + modulesMap.forEach(module => { + const moduleName = module.name; + if (!graph.hasNode(moduleName)) { + graph.addNode(moduleName); + } + }); - const visitedModulesToAddDependency = new Set(); + const visitedModulesToAddDependency = new Set(); - const visitModuleToAddDependency = (module: GraphQLModule) => { - for (let subModule of module.selfImports) { - const subModuleOrigName = typeof subModule === 'string' ? subModule : subModule.name; - subModule = modulesMap.get(subModuleOrigName); - if (!subModule) { - throw new DependencyModuleNotFoundError(subModuleOrigName, module.name); - } - try { - graph.addDependency( - module.name, - subModule.name, - ); - } catch (e) { - throw new DependencyModuleNotFoundError(subModuleOrigName, module.name); - } - // prevent infinite loop in case of circular dependency - if (!visitedModulesToAddDependency.has(subModule.name)) { - visitedModulesToAddDependency.add(subModule.name); - visitModuleToAddDependency(subModule); - } + const visitModuleToAddDependency = (module: GraphQLModule) => { + for (let subModule of module.selfImports) { + const subModuleOrigName = typeof subModule === 'string' ? subModule : subModule.name; + subModule = modulesMap.get(subModuleOrigName); + if (!subModule) { + throw new DependencyModuleNotFoundError(subModuleOrigName, module.name); } - }; + try { + graph.addDependency( + module.name, + subModule.name, + ); + } catch (e) { + throw new DependencyModuleNotFoundError(subModuleOrigName, module.name); + } + // prevent infinite loop in case of circular dependency + if (!visitedModulesToAddDependency.has(subModule.name)) { + visitedModulesToAddDependency.add(subModule.name); + visitModuleToAddDependency(subModule); + } + } + }; - visitModuleToAddDependency(modulesMap.get(this.name)); - - try { - graph.overallOrder(); - return modulesMap; - } catch (e) { - const { message } = e as Error; - const currentPathStr = message.replace('Dependency Cycle Found: ', ''); - const currentPath = currentPathStr.split(' -> '); - const moduleIndexMap = new Map(); - let start = 0; - let end = currentPath.length; - currentPath.forEach((moduleName, index) => { - if (moduleIndexMap.has(moduleName)) { - start = moduleIndexMap.get(moduleName); - end = index; - } else { - moduleIndexMap.set(moduleName, index); - } - }); - const realPath = currentPath.slice(start, end); - const circularModules = Array.from(new Set(realPath)).map(moduleName => { - // if it is merged module, get one module, it will be enough to get merged one. - return modulesMap.get(moduleName); - }); - const mergedModule = GraphQLModule.mergeModules(circularModules, modulesMap); - for (const moduleName of realPath) { - modulesMap.set(moduleName, mergedModule); - for (const subModuleName of moduleName.split('+')) { - if (modulesMap.has(subModuleName)) { - modulesMap.set(subModuleName, mergedModule); - } + visitModuleToAddDependency(modulesMap.get(this.name)); + + try { + graph.overallOrder(); + return modulesMap; + } catch (e) { + const { message } = e as Error; + const currentPathStr = message.replace('Dependency Cycle Found: ', ''); + const currentPath = currentPathStr.split(' -> '); + const moduleIndexMap = new Map(); + let start = 0; + let end = currentPath.length; + currentPath.forEach((moduleName, index) => { + if (moduleIndexMap.has(moduleName)) { + start = moduleIndexMap.get(moduleName); + end = index; + } else { + moduleIndexMap.set(moduleName, index); + } + }); + const realPath = currentPath.slice(start, end); + const circularModules = Array.from(new Set(realPath)).map(moduleName => { + // if it is merged module, get one module, it will be enough to get merged one. + return modulesMap.get(moduleName); + }); + const mergedModule = GraphQLModule.mergeModules(circularModules, modulesMap); + for (const moduleName of realPath) { + modulesMap.set(moduleName, mergedModule); + for (const subModuleName of moduleName.split('+')) { + if (modulesMap.has(subModuleName)) { + modulesMap.set(subModuleName, mergedModule); } } - modulesMap.set(mergedModule.name, mergedModule); - (mergedModule._options.imports as Array>) - = (mergedModule._options.imports as Array>).filter( + } + modulesMap.set(mergedModule.name, mergedModule); + (mergedModule._options.imports as Array>) + = (mergedModule._options.imports as Array>).filter( module => { const moduleName = typeof module === 'string' ? module : module.name; module = modulesMap.get(moduleName); return (module.name !== mergedModule.name); }, ); - return this.checkAndFixModulesMap(modulesMap); - } + return this.checkAndFixModulesMap(modulesMap); } + } - static mergeModules(modules: Array>, modulesMap?: ModulesMap): GraphQLModule { - const nameSet = new Set(); - const typeDefsSet = new Set(); - const resolversSet = new Set(); - const contextBuilderSet = new Set>(); - const importsSet = new Set>(); - const providersSet = new Set>(); - const resolversCompositionSet = new Set(); - const schemaDirectivesSet = new Set(); - for (const module of modules) { - const subMergedModuleNames = module.name.split('+'); - for (const subMergedModuleName of subMergedModuleNames) { - nameSet.add(subMergedModuleName); - } - if (Array.isArray(module.selfTypeDefs)) { - for (const typeDef of module.selfTypeDefs) { - typeDefsSet.add(typeDef); - } - } else { - typeDefsSet.add(module.selfTypeDefs); - } - resolversSet.add(module.selfResolvers); - contextBuilderSet.add(module._options.contextBuilder); - for (let importModule of module.selfImports) { - if (modulesMap) { - importModule = modulesMap.get(typeof importModule === 'string' ? importModule : importModule.name); - } - importsSet.add(importModule); + static mergeModules(modules: Array>, modulesMap?: ModulesMap): GraphQLModule { + const nameSet = new Set(); + const typeDefsSet = new Set(); + const resolversSet = new Set(); + const contextBuilderSet = new Set>(); + const importsSet = new Set>(); + const providersSet = new Set>(); + const resolversCompositionSet = new Set(); + const schemaDirectivesSet = new Set(); + for (const module of modules) { + const subMergedModuleNames = module.name.split('+'); + for (const subMergedModuleName of subMergedModuleNames) { + nameSet.add(subMergedModuleName); + } + if (Array.isArray(module.selfTypeDefs)) { + for (const typeDef of module.selfTypeDefs) { + typeDefsSet.add(typeDef); } - for (const provider of module.selfProviders) { - providersSet.add(provider); + } else { + typeDefsSet.add(module.selfTypeDefs); + } + resolversSet.add(module.selfResolvers); + contextBuilderSet.add(module._options.contextBuilder); + for (let importModule of module.selfImports) { + if (modulesMap) { + importModule = modulesMap.get(typeof importModule === 'string' ? importModule : importModule.name); } - resolversCompositionSet.add(module.selfResolversComposition); - schemaDirectivesSet.add(module.selfSchemaDirectives); + importsSet.add(importModule); } - - const name = [...nameSet].join('+'); - const typeDefs = [...typeDefsSet]; - const resolvers = mergeResolvers([...resolversSet]); - const contextBuilder = [...contextBuilderSet].reduce( - (accContextBuilder, currentContextBuilder) => { - return async (networkRequest, currentContext, injector) => { - const accContext = await accContextBuilder(networkRequest, currentContext, injector); - const moduleContext = currentContextBuilder ? await currentContextBuilder(networkRequest, currentContext, injector) : {}; - return Object.assign({}, accContext, moduleContext); - }; - }, - ); - const imports = [...importsSet]; - const providers = [...providersSet]; - const resolversComposition = deepmerge.all([...resolversCompositionSet]); - const schemaDirectives = deepmerge.all([...schemaDirectivesSet]) as ISchemaDirectives; - return new GraphQLModule({ - name, - typeDefs, - resolvers, - contextBuilder, - imports, - providers, - resolversComposition, - schemaDirectives, - }); + for (const provider of module.selfProviders) { + providersSet.add(provider); + } + resolversCompositionSet.add(module.selfResolversComposition); + schemaDirectivesSet.add(module.selfSchemaDirectives); } + + const name = [...nameSet].join('+'); + const typeDefs = [...typeDefsSet]; + const resolvers = mergeResolvers([...resolversSet]); + const contextBuilder = [...contextBuilderSet].reduce( + (accContextBuilder, currentContextBuilder) => { + return async (networkRequest, currentContext, injector) => { + const accContext = await accContextBuilder(networkRequest, currentContext, injector); + const moduleContext = currentContextBuilder ? await currentContextBuilder(networkRequest, currentContext, injector) : {}; + return Object.assign({}, accContext, moduleContext); + }; + }, + ); + const imports = [...importsSet]; + const providers = [...providersSet]; + const resolversComposition = deepmerge.all([...resolversCompositionSet]); + const schemaDirectives = deepmerge.all([...schemaDirectivesSet]) as ISchemaDirectives; + return new GraphQLModule({ + name, + typeDefs, + resolvers, + contextBuilder, + imports, + providers, + resolversComposition, + schemaDirectives, + }); } +}