diff --git a/.eslintrc b/.eslintrc index d6253f1..04889ec 100644 --- a/.eslintrc +++ b/.eslintrc @@ -4,9 +4,5 @@ "no-console": "off", "global-require": "off", "no-plusplus": "off" - }, - "env": { - "mocha": true } } - \ No newline at end of file diff --git a/generateModule.js b/generateModule.js index 9aaa2bb..1fac8c5 100755 --- a/generateModule.js +++ b/generateModule.js @@ -1,12 +1,15 @@ #!/usr/bin/env node const finder = require('find-package-json'); const shelljs = require('shelljs'); +const { Source, buildSchema } = require('graphql'); -const getModuleInfos = require('./parsegraphql/getModuleInfos'); -const getModuleNames = require('./parsegraphql/getModuleNames'); -const checkIfGitStateClean = require('./helpers/checkIfGitStateClean'); +const getModuleInfos = require('./parse-graphql/getModuleInfos'); +const getModuleNames = require('./parse-graphql/getModuleNames'); +const getFederatedEntities = require('./parse-graphql/getFederatedEntities'); +// const checkIfGitStateClean = require('./helpers/checkIfGitStateClean'); const saveRenderedTemplate = require('./helpers/saveRenderedTemplate'); -checkIfGitStateClean(); + +const capitalize = (string) => string.charAt(0).toUpperCase() + string.slice(1); const f = finder(process.cwd()); const projectMainPath = f @@ -17,33 +20,73 @@ const projectMainPath = f const graphqlPaths = shelljs.ls(`${projectMainPath}/src/modules/*/graphql/*.graphql`).map((p) => ({ name: p })); +shelljs.mkdir('-p', `${projectMainPath}/generated/graphql`); + +// "Framework" "generated" files - initial generation +const createCombineSchemas = () => { + const templateName = './templates/combineSchemas.ts'; + const filePath = `${projectMainPath}/generated/graphql/`; + const fileName = `combineSchemas.ts`; + + saveRenderedTemplate(templateName, {}, filePath, fileName); +}; + +createCombineSchemas(); + +const createPrintSchema = () => { + const templateName = './templates/printSchema.ts'; + const filePath = `${projectMainPath}/generated/graphql/`; + const fileName = `printSchema.ts`; + + saveRenderedTemplate(templateName, {}, filePath, fileName); +} + +createPrintSchema(); + +const createGenericDataModelSchema = () => { + const templateName = './templates/genericDataModelSchema.graphql'; + const filePath = `${projectMainPath}/generated/graphql/`; + const fileName = `genericDataModelSchema.graphql`; + + saveRenderedTemplate(templateName, {}, filePath, fileName); +} + +createGenericDataModelSchema(); + +const createFrameworkSchema = () => { + const templateName = './templates/frameworkSchema.graphql'; + const filePath = `${projectMainPath}/generated/graphql/`; + const fileName = `frameworkSchema.graphql`; + + saveRenderedTemplate(templateName, {}, filePath, fileName); +} + +createFrameworkSchema(); + +// End of "Framework" "generated" files + +// Initial App Setup files + +// + const moduleNames = getModuleNames(graphqlPaths); const modules = getModuleInfos(moduleNames); modules.forEach((module) => { const moduleName = module.name; - const createModuleResolvers = () => { - const templateName = './templates/moduleResolvers.handlebars'; - const context = { ...module, moduleName: module.name }; - const filePath = `${projectMainPath}/src/modules/${moduleName}/graphql/`; - const fileName = `${moduleName}Resolvers.ts`; - saveRenderedTemplate(templateName, context, filePath, fileName); - }; - - createModuleResolvers(); - const createQuery = (queryName) => { + const createQuery = (queryName, hasArguments) => { const templateName = './templates/query.handlebars'; - const context = { queryName, moduleName }; + const context = { queryName, moduleName, hasArguments }; const filePath = `${projectMainPath}/src/modules/${moduleName}/graphql/queries/`; const fileName = `${queryName}Query.ts`; const keepIfExists = true; saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); }; - const createQuerySpec = (queryName) => { + const createQuerySpec = (queryName, hasArguments) => { const templateName = './templates/query.spec.handlebars'; - const context = { queryName, moduleName }; + const context = { queryName, moduleName, hasArguments }; const filePath = `${projectMainPath}/src/modules/${moduleName}/graphql/queries/`; const fileName = `${queryName}Query.spec.ts`; const keepIfExists = true; @@ -52,24 +95,24 @@ modules.forEach((module) => { if (module.queries && module.queries.length) { shelljs.mkdir('-p', `${projectMainPath}/src/modules/${moduleName}/graphql/queries`); - module.queries.forEach(({ name }) => { - createQuery(name); - createQuerySpec(name); + module.queries.forEach(({ name, hasArguments, variables }) => { + createQuery(name, hasArguments); + createQuerySpec(name, hasArguments); }); } - const createMutation = (mutationName) => { + const createMutation = (mutationName, hasArguments) => { const templateName = './templates/mutation.handlebars'; - const context = { mutationName, moduleName }; + const context = { mutationName, moduleName, hasArguments }; const filePath = `${projectMainPath}/src/modules/${moduleName}/graphql/mutations/`; const fileName = `${mutationName}Mutation.ts`; const keepIfExists = true; saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); }; - const createMutationSpec = (mutationName) => { + const createMutationSpec = (mutationName, hasArguments) => { const templateName = './templates/mutation.spec.handlebars'; - const context = { mutationName, moduleName }; + const context = { mutationName, moduleName, hasArguments }; const filePath = `${projectMainPath}/src/modules/${moduleName}/graphql/mutations/`; const fileName = `${mutationName}Mutation.spec.ts`; const keepIfExists = true; @@ -78,9 +121,9 @@ modules.forEach((module) => { if (module.mutations && module.mutations.length) { shelljs.mkdir('-p', `${projectMainPath}/src/modules/${moduleName}/graphql/mutations`); - module.mutations.forEach(({ name }) => { - createMutation(name); - createMutationSpec(name); + module.mutations.forEach(({ name, hasArguments, variables }) => { + createMutation(name, hasArguments); + createMutationSpec(name, hasArguments); }); } }); @@ -88,87 +131,128 @@ modules.forEach((module) => { const createGlobalResolvers = () => { const templateName = './templates/resolvers.handlebars'; const context = { modules }; - const filePath = `${projectMainPath}/src/graphql/`; + const filePath = `${projectMainPath}/generated/graphql/`; const fileName = `resolvers.ts`; saveRenderedTemplate(templateName, context, filePath, fileName); }; createGlobalResolvers(); -const createTypes = () => { - const templateName = './templates/types.handlebars'; - const context = { modules }; +const createRoot = () => { + const templateName = './templates/root.handlebars'; const filePath = `${projectMainPath}/src/`; - const fileName = `types.ts`; - saveRenderedTemplate(templateName, context, filePath, fileName); -}; - -createTypes(); + const fileName = `root.ts`; + const keepIfExists = true; -const createStartupConfig = () => { - const templateName = './templates/startupConfig.handlebars'; - const context = { modules }; - const filePath = `${projectMainPath}/src/`; - const fileName = `startupConfig.ts`; - saveRenderedTemplate(templateName, context, filePath, fileName); + saveRenderedTemplate(templateName, {}, filePath, fileName, keepIfExists); }; -createStartupConfig(); +createRoot(); -const createIModuleNameContexts = () => { - modules.forEach(({ name }) => { - const templateName = './templates/IModuleNameContext.handlebars'; - const context = { moduleName: name }; - const filePath = `${projectMainPath}/src/modules/${name}/`; - const fileName = `I${name}Context.ts`; - const keepIfExists = true; +const createContext = () => { + const templateName = './templates/context.handlebars'; + const filePath = `${projectMainPath}/src/`; + const fileName = `context.ts`; + const keepIfExists = true; - saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); - }); + saveRenderedTemplate(templateName, {}, filePath, fileName, keepIfExists); }; -createIModuleNameContexts(); +createContext(); -const createGetModuleNameContexts = () => { - modules.forEach(({ name }) => { - const templateName = './templates/getModuleNameContext.handlebars'; - const context = { moduleName: name }; - const filePath = `${projectMainPath}/src/modules/${name}/`; - const fileName = `get${name}Context.ts`; - const keepIfExists = true; +const createTypeResolvers = () => { + modules.forEach(({ name, typeDefinitions, types, schemaString, queries, mutations }) => { + let typeResolvers = []; + if (types) { + const federatedEntities = getFederatedEntities(schemaString); + schemaString = schemaString.replace(/extend type/g, `type`); + let source = new Source(schemaString); + let schema = buildSchema(source); + shelljs.mkdir('-p', `${projectMainPath}/src/modules/${name}/graphql/types/`); + typeDefinitions.forEach((typeDef) => { + let filtered = []; + let type = schema.getType(typeDef.name); + if (!type) { + const newSchemaString = schemaString.replace(`extend type ${typeDef.name}`, `type ${typeDef.name}`); + let source = new Source(newSchemaString); + let schema = buildSchema(source); + type = schema.getType(typeDef.name); + } + if (type.astNode) { + if (type.astNode.directives && !!type.astNode.directives.find((d) => d.name.value === 'entity')) { + filtered = type.astNode.fields.filter( + (f) => + !f.directives.find( + (d) => + d.name.value === 'column' || + d.name.value === 'id' || + d.name.value === 'embedded' || + d.name.value === 'external' + ) + ); + } else { + filtered = type.astNode.fields.filter((f) => f.directives.find((d) => d.name.value === 'computed')); + } + } - saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); - }); -}; + if (federatedEntities.find((e) => e === typeDef.name)) { + filtered = filtered.concat(federatedEntities.map((e) => ({ name: { value: '__resolveReference' }, resolveReferenceType: true }))); + } -createGetModuleNameContexts(); -// typeResolvers.handlebars + filtered.forEach(({ name: { value }, resolveReferenceType }) => { + const templateName = './templates/typeTypeResolvers.handlebars'; + let capitalizedFieldName = capitalize(value); + const context = { + typeName: typeDef.name, + fieldName: value, + moduleName: name, + resolveReferenceType, + capitalizedFieldName, + }; + const filePath = `${projectMainPath}/src/modules/${name}/graphql/types/`; + const fileName = `${typeDef.name}${capitalizedFieldName}.ts`; + const keepIfExists = true; + saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); + }); -const createTypeResolvers = () => { - modules.forEach(({ name, typeDefinitions, types }) => { - if (types) { - shelljs.mkdir('-p', `${projectMainPath}/src/modules/${name}/graphql/types/`); - const templateName = './templates/typeResolvers.handlebars'; - const context = { type: typeDefinitions }; - const filePath = `${projectMainPath}/src/modules/${name}/graphql/types/`; - const fileName = `typeResolvers.ts`; - const keepIfExists = false; + filtered.forEach(({ name: { value }, arguments, resolveReferenceType }) => { + const templateName = './templates/typeTypeResolvers.spec.handlebars'; + let capitalizedFieldName = capitalize(value); + const context = { + typeName: typeDef.name, + fieldName: value, + moduleName: name, + hasArguments: arguments && arguments.length, + resolveReferenceType, + capitalizedFieldName, + }; + const filePath = `${projectMainPath}/src/modules/${name}/graphql/types/`; + const fileName = `${typeDef.name}${capitalizedFieldName}.spec.ts`; + const keepIfExists = true; - saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); - typeDefinitions.forEach((typeDef) => { - const templateName = './templates/typeTypeResolvers.handlebars'; - const context = { typeName: typeDef.name }; - const filePath = `${projectMainPath}/src/modules/${name}/graphql/types/`; - const fileName = `${typeDef.name}TypeResolvers.ts`; - const keepIfExists = true; + saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); + }); - saveRenderedTemplate(templateName, context, filePath, fileName, keepIfExists); + if (filtered.length) { + typeResolvers.push({ + typeName: typeDef.name, + fieldName: filtered.map(({ name: { value } }) => ({ name: value, capitalizedName: capitalize(value) })), + }); + } }); } + const moduleName = name; + const createModuleResolvers = () => { + const templateName = './templates/moduleResolvers.handlebars'; + const context = { moduleName, queries, mutations, typeResolvers }; + const filePath = `${projectMainPath}/generated/graphql/`; + const fileName = `${moduleName}Resolvers.ts`; + saveRenderedTemplate(templateName, context, filePath, fileName); + }; + createModuleResolvers(); }); }; createTypeResolvers(); - diff --git a/helpers/checkIfGitStateClean.js b/helpers/checkIfGitStateClean.js index 4c0df54..5aefc93 100644 --- a/helpers/checkIfGitStateClean.js +++ b/helpers/checkIfGitStateClean.js @@ -32,7 +32,7 @@ module.exports = function checkIfGitStateClean() { const status = statusSync(currentPath); if (status.changed && !process.env.IGNORE_GIT) { - console.error(`We are about to generate and modify some code, but you have ${status.dirty} modified and ${status.untracked} files. We are counting only files in src/ and excluding .graphql. + console.error(`We are about to generate and modify some code, but you have ${status.dirty} modified and ${status.untracked} new files. We are counting only files in src/ and excluding .graphql. To make sure you can easily verify and revert the changes introduced by this tool, please commit or stash the existing changes. If you want to ignore this warning run the tooling with IGNORE_GIT=true. This is STRONGLY discouraged!`); process.exit(2); diff --git a/helpers/saveRenderedTemplate.js b/helpers/saveRenderedTemplate.js index c96dc11..5fb680a 100644 --- a/helpers/saveRenderedTemplate.js +++ b/helpers/saveRenderedTemplate.js @@ -2,6 +2,10 @@ const fs = require('fs'); const Handlebars = require('handlebars'); const path = require('path'); +Handlebars.registerHelper('toUpperCase', function(str) { + return str.replace(/^\w/, c => c.toUpperCase()); +}); + module.exports = function saveRenderedTemplate (templateName, context, filePath, fileName, keepIfExists = false) { const combinedPath = path.join(filePath, fileName); if (keepIfExists && fs.existsSync(combinedPath)) { diff --git a/index.js b/index.js index 6249240..8ca3b2c 100755 --- a/index.js +++ b/index.js @@ -1,200 +1,34 @@ #!/usr/bin/env node -const { Source, buildSchema } = require('graphql'); -const gql = require('graphql-tag'); +const fs = require("fs"); -const fs = require('fs'); -const path = require('path'); const program = require('commander'); -const del = require('del'); +const shelljs = require('shelljs'); program - .option('--schemaFilePath [value]', 'path of your graphql schema file') - .option('--destDirPath [value]', 'dir you want to store the generated queries') - .option('--depthLimit [value]', 'query depth you want to limit(The default is 100)') - .option('--typesPath [value]', 'path to your generated typescript file with GraphQL Types') + .option('--create ', 'Create a new app - pass a name') + .option('--codeGen', 'Generate code') .parse(process.argv); -console.log(); -const { schemaFilePath, destDirPath, typesPath, depthLimit = 100 } = program; +const { create, codeGen } = program; -const pathToDestDir = `${process.cwd()}${destDirPath}`; -const pathToTypes = `${process.cwd()}${typesPath}`; -const typesRelativePathWithExtension = path.relative(pathToDestDir, pathToTypes); -const typesRelativePath = typesRelativePathWithExtension.replace(path.extname(typesRelativePathWithExtension), ''); +const scaffoldDir = `${process.cwd()}/${create}`; -const typeDef = fs.readFileSync(schemaFilePath); - -let gqlSchema -try { - const source = new Source(typeDef); - gqlSchema = buildSchema(source); -} catch(e) { - const { buildFederatedSchema } = require('@apollo/federation'); - - gqlSchema = buildFederatedSchema([ - { - typeDefs: gql` - ${typeDef} - `, - }, - ]); -} - -del.sync(destDirPath); -path - .resolve(destDirPath) - .split(path.sep) - .reduce((before, cur) => { - const pathTmp = path.join(before, cur + path.sep); - if (!fs.existsSync(pathTmp)) { - fs.mkdirSync(pathTmp); - } - return path.join(before, cur + path.sep); - }, ''); - -/** - * Generate the query for the specified field - * @param curName name of the current field - * @param curParentType parent type of the current field - * @param curParentName parent name of the current field - * @param argumentList list of arguments from all fields - * @param crossReferenceKeyList list of the cross reference - * @param curDepth currentl depth of field - */ -const generateQuery = ( - curName, - curParentType, - curParentName, - argumentList = [], - crossReferenceKeyList = [], // [`${curParentName}To${curName}Key`] - curDepth = 1 -) => { - const field = gqlSchema.getType(curParentType).getFields()[curName]; - const curTypeName = field.type.inspect().replace(/[[\]!]/g, ''); - const curType = gqlSchema.getType(curTypeName); - let queryStr = ''; - let childQuery = ''; - - if (curType.getFields) { - const crossReferenceKey = `${curParentName}To${curName}Key`; - if (crossReferenceKeyList.indexOf(crossReferenceKey) !== -1 || curDepth > depthLimit) return ''; - crossReferenceKeyList.push(crossReferenceKey); - const childKeys = Object.keys(curType.getFields()); - childQuery = childKeys - .map((cur) => generateQuery(cur, curType, curName, argumentList, crossReferenceKeyList, curDepth + 1).queryStr) - .filter((cur) => cur) - .join('\n'); - } - - if (!(curType.getFields && !childQuery)) { - queryStr = `${' '.repeat(curDepth)}${field.name}`; - if (field.args.length > 0) { - argumentList.push(...field.args); - const argsStr = field.args.map((arg) => `${arg.name}: $${arg.name}`).join(', '); - queryStr += `(${argsStr})`; - } - if (childQuery) { - queryStr += `{\n${childQuery}\n${' '.repeat(curDepth)}}`; - } - } - - /* Union types */ - if (curType.astNode && curType.astNode.kind === 'UnionTypeDefinition') { - const types = curType.getTypes(); - if (types && types.length) { - const indent = `${' '.repeat(curDepth)}`; - const fragIndent = `${' '.repeat(curDepth + 1)}`; - queryStr += '{\n'; - - for (let i = 0, len = types.length; i < len; i++) { - const valueTypeName = types[i]; - const valueType = gqlSchema.getType(valueTypeName); - const unionChildQuery = Object.keys(valueType.getFields()) - .map( - (cur) => generateQuery(cur, valueType, curName, argumentList, crossReferenceKeyList, curDepth + 2).queryStr - ) - .filter((cur) => cur) - .join('\n'); - queryStr += `${fragIndent}... on ${valueTypeName} {\n${unionChildQuery}\n${fragIndent}}\n`; - } - queryStr += `${indent}}`; - } +if (create) { + if (fs.existsSync(scaffoldDir)) { + console.log(`Path: ${scaffoldDir} already exists. Can't create a new app in an already existing path.`) + process.exit(1) } - return { queryStr, argumentList }; -}; - -const templateContext = { mutations: [], queries: [], subscriptions: [], pathToTypes: typesRelativePath }; - -/** - * Generate the query for the specified field - * @param obj one of the root objects(Query, Mutation, Subscription) - * @param description description of the current object - */ -const generateFile = (obj, description) => { - let indexJs = "import gql from \"graphql-tag\";\nconst fs = require('fs');\nconst path = require('path');\n\n"; - let outputFolderName; - switch (description) { - case 'Mutation': - outputFolderName = 'mutations'; - break; - case 'Query': - outputFolderName = 'queries'; - break; - case 'Subscription': - outputFolderName = 'subscriptions'; - break; - default: - console.log('[gqlg warning]:', 'description is required'); - } - const writeFolder = path.join(destDirPath, `./${outputFolderName}`); - fs.mkdirSync(writeFolder); - Object.keys(obj) - .filter((t) => t !== '_entities' && t !== '_service') - .forEach((type) => { - console.log(type); - const queryResult = generateQuery(type, description); - let query = queryResult.queryStr; - const field = gqlSchema.getType(description).getFields()[type]; - const args = field.args.concat(queryResult.argumentList); - const argStr = args - .filter((item, pos) => args.indexOf(item) === pos) - .map((arg) => `$${arg.name}: ${arg.type}`) - .join(', '); - query = `${description.toLowerCase()} ${type}${argStr ? `(${argStr})` : ''}{\n${query}\n}`; - fs.writeFileSync(path.join(writeFolder, `./${type}.graphql`), query); - indexJs += `export const ${type} = gql\`$\{fs.readFileSync(path.join(__dirname, '${type}.graphql'), 'utf8')}\`;\n`; - templateContext[outputFolderName].push({ name: type, hasVariables: !!argStr }); - }); - fs.writeFileSync(path.join(writeFolder, 'index.ts'), indexJs); -}; - -if (gqlSchema.getMutationType()) { - generateFile(gqlSchema.getMutationType().getFields(), 'Mutation'); -} else { - console.log('[gqlg warning]:', 'No mutation type found in your schema'); + shelljs.cp('-R', `${__dirname}/scaffold`, `${scaffoldDir}`) + shelljs.exec(`cd ${create} && git init .`) + console.log(`\n${create} created successfully!`) + console.log(`run: + cd ${create} + npm install`) + console.log('and start hacking! :-)') + process.exit(1) } -if (gqlSchema.getQueryType()) { - generateFile(gqlSchema.getQueryType().getFields(), 'Query'); -} else { - console.log('[gqlg warning]:', 'No query type found in your schema'); -} -if (gqlSchema.getSubscriptionType()) { - generateFile(gqlSchema.getSubscriptionType().getFields(), 'Subscription'); -} else { - console.log('[gqlg warning]:', 'No subscription type found in your schema'); +if (codeGen) { + require("./generateModule") } -const Handlebars = require('handlebars'); - -Handlebars.registerHelper('toUpperCase', function (s) { - if (typeof s !== 'string') return ''; - return s.charAt(0).toUpperCase() + s.slice(1); -}); - -const rootTemplate = fs.readFileSync(path.join(__dirname, './root.handlebars'), 'utf8'); - -const generateIndex = () => Handlebars.compile(rootTemplate)(templateContext); - -fs.writeFileSync(path.join(destDirPath, 'index.ts'), generateIndex()); -require('./generateModule'); diff --git a/package-lock.json b/package-lock.json index c31a8e3..3aafc7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,9 @@ { "name": "gql-typescript-generator", - "version": "6.0.0", + "version": "9.0.5", "lockfileVersion": 1, "requires": true, "dependencies": { - "@apollo/federation": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@apollo/federation/-/federation-0.16.2.tgz", - "integrity": "sha512-pjTkcl1KGxLZOPpVyTygZNuLxZJCCMvGVonPJMoFzQYt63/o0DwpwbcNlbvpdryWjjFgvi5diqJxRFGuxndEPA==", - "requires": { - "apollo-graphql": "^0.4.0", - "apollo-server-env": "^2.4.4", - "core-js": "^3.4.0", - "lodash.xorby": "^4.7.0" - } - }, "@babel/code-frame": { "version": "7.0.0", "resolved": "http://registry.npm.taobao.org/@babel/code-frame/download/@babel/code-frame-7.0.0.tgz", @@ -1217,28 +1206,8 @@ "@types/node": { "version": "14.0.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.1.tgz", - "integrity": "sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==" - }, - "@types/node-fetch": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", - "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", - "requires": { - "@types/node": "*", - "form-data": "^3.0.0" - }, - "dependencies": { - "form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } - } + "integrity": "sha512-FAYBGwC+W6F9+huFIDtn43cpy7+SzG+atzRiTfdp3inUKL2hXnd4rG8hylJLIh4+hqrQy1P17kvJByE/z825hA==", + "dev": true }, "@types/normalize-package-data": { "version": "2.4.0", @@ -1366,35 +1335,6 @@ "picomatch": "^2.0.4" } }, - "apollo-env": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/apollo-env/-/apollo-env-0.6.5.tgz", - "integrity": "sha512-jeBUVsGymeTHYWp3me0R2CZRZrFeuSZeICZHCeRflHTfnQtlmbSXdy5E0pOyRM9CU4JfQkKDC98S1YglQj7Bzg==", - "requires": { - "@types/node-fetch": "2.5.7", - "core-js": "^3.0.1", - "node-fetch": "^2.2.0", - "sha.js": "^2.4.11" - } - }, - "apollo-graphql": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/apollo-graphql/-/apollo-graphql-0.4.4.tgz", - "integrity": "sha512-i012iRKT5nfsOaNMx4MTwHw2jrlyaF1zikpejxsGHsKIf3OngGvGh3pyw20bEmwj413OrNQpRxvvIz5A7W/8xw==", - "requires": { - "apollo-env": "^0.6.5", - "lodash.sortby": "^4.7.0" - } - }, - "apollo-server-env": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/apollo-server-env/-/apollo-server-env-2.4.4.tgz", - "integrity": "sha512-c2oddDS3lwAl6QNCIKCLEzt/dF9M3/tjjYRVdxOVN20TidybI7rAbnT4QOzf4tORnGXtiznEAvr/Kc9ahhKADg==", - "requires": { - "node-fetch": "^2.1.2", - "util.promisify": "^1.0.0" - } - }, "argparse": { "version": "1.0.10", "resolved": "http://registry.npm.taobao.org/argparse/download/argparse-1.0.10.tgz", @@ -1426,6 +1366,7 @@ "version": "1.0.2", "resolved": "http://registry.npm.taobao.org/array-union/download/array-union-1.0.2.tgz", "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, "requires": { "array-uniq": "^1.0.1" } @@ -1433,7 +1374,8 @@ "array-uniq": { "version": "1.0.3", "resolved": "http://registry.npm.taobao.org/array-uniq/download/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true }, "array-unique": { "version": "0.3.2", @@ -1479,7 +1421,8 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true }, "atob": { "version": "2.1.2", @@ -1718,12 +1661,6 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", "dev": true }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "http://registry.npm.taobao.org/browser-stdout/download/browser-stdout-1.3.1.tgz", - "integrity": "sha1-uqVZ7hTO1zRSIputcyZGfGH6vWA=", - "dev": true - }, "bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", @@ -1974,6 +1911,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -2015,11 +1953,6 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, - "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==" - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -2082,15 +2015,6 @@ "whatwg-url": "^8.0.0" } }, - "debug": { - "version": "3.1.0", - "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", @@ -2125,6 +2049,7 @@ "version": "1.1.3", "resolved": "http://registry.npm.taobao.org/define-properties/download/define-properties-1.1.3.tgz", "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", + "dev": true, "requires": { "object-keys": "^1.0.12" } @@ -2170,23 +2095,11 @@ } } }, - "del": { - "version": "3.0.0", - "resolved": "http://registry.npm.taobao.org/del/download/del-3.0.0.tgz", - "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", - "requires": { - "globby": "^6.1.0", - "is-path-cwd": "^1.0.0", - "is-path-in-cwd": "^1.0.0", - "p-map": "^1.1.1", - "pify": "^3.0.0", - "rimraf": "^2.2.8" - } - }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true }, "detect-newline": { "version": "3.1.0", @@ -2194,12 +2107,6 @@ "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true }, - "diff": { - "version": "3.5.0", - "resolved": "http://registry.npm.taobao.org/diff/download/diff-3.5.0.tgz", - "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", - "dev": true - }, "diff-sequences": { "version": "26.0.0", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz", @@ -2925,7 +2832,8 @@ "function-bind": { "version": "1.1.1", "resolved": "http://registry.npm.taobao.org/function-bind/download/function-bind-1.1.1.tgz", - "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=" + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", + "dev": true }, "functional-red-black-tree": { "version": "1.0.1", @@ -2996,25 +2904,6 @@ "integrity": "sha1-we9F7pvta63wZjxcuQ6NGt7BMh0=", "dev": true }, - "globby": { - "version": "6.1.0", - "resolved": "http://registry.npm.taobao.org/globby/download/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "http://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } - } - }, "graceful-fs": { "version": "4.1.11", "resolved": "http://registry.npm.taobao.org/graceful-fs/download/graceful-fs-4.1.11.tgz", @@ -3034,12 +2923,6 @@ "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.10.3.tgz", "integrity": "sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA==" }, - "growl": { - "version": "1.10.3", - "resolved": "http://registry.npm.taobao.org/growl/download/growl-1.10.3.tgz", - "integrity": "sha1-GSa6kM8+3+KttJJ/WIC8IsZseQ8=", - "dev": true - }, "growly": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", @@ -3098,20 +2981,16 @@ "version": "1.0.3", "resolved": "http://registry.npm.taobao.org/has/download/has-1.0.3.tgz", "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", + "dev": true, "requires": { "function-bind": "^1.1.1" } }, - "has-flag": { - "version": "2.0.0", - "resolved": "http://registry.npm.taobao.org/has-flag/download/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, "has-symbols": { "version": "1.0.0", "resolved": "http://registry.npm.taobao.org/has-symbols/download/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true }, "has-value": { "version": "1.0.0", @@ -3165,12 +3044,6 @@ } } }, - "he": { - "version": "1.1.1", - "resolved": "http://registry.npm.taobao.org/he/download/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, "hosted-git-info": { "version": "2.7.1", "resolved": "http://registry.npm.taobao.org/hosted-git-info/download/hosted-git-info-2.7.1.tgz", @@ -3425,7 +3298,8 @@ "is-date-object": { "version": "1.0.1", "resolved": "http://registry.npm.taobao.org/is-date-object/download/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true }, "is-descriptor": { "version": "0.1.6", @@ -3480,12 +3354,14 @@ "is-path-cwd": { "version": "1.0.0", "resolved": "http://registry.npm.taobao.org/is-path-cwd/download/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=" + "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", + "dev": true }, "is-path-in-cwd": { "version": "1.0.1", "resolved": "http://registry.npm.taobao.org/is-path-in-cwd/download/is-path-in-cwd-1.0.1.tgz", "integrity": "sha1-WsSLNF72dTOb1sekipEhELJBz1I=", + "dev": true, "requires": { "is-path-inside": "^1.0.0" } @@ -3494,6 +3370,7 @@ "version": "1.0.1", "resolved": "http://registry.npm.taobao.org/is-path-inside/download/is-path-inside-1.0.1.tgz", "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, "requires": { "path-is-inside": "^1.0.1" } @@ -3556,6 +3433,7 @@ "version": "1.0.2", "resolved": "http://registry.npm.taobao.org/is-symbol/download/is-symbol-1.0.2.tgz", "integrity": "sha1-oFX2rlcZLK7jKeeoYBGLSXqVDzg=", + "dev": true, "requires": { "has-symbols": "^1.0.0" } @@ -5401,12 +5279,8 @@ "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" - }, - "lodash.xorby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.xorby/-/lodash.xorby-4.7.0.tgz", - "integrity": "sha1-nBmm+fBjputT3QPBtocXmYAUY9c=" + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true }, "make-dir": { "version": "3.1.0", @@ -5468,12 +5342,14 @@ "mime-db": { "version": "1.44.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "dev": true }, "mime-types": { "version": "2.1.27", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "dev": true, "requires": { "mime-db": "1.44.0" } @@ -5527,32 +5403,6 @@ "minimist": "0.0.8" } }, - "mocha": { - "version": "5.0.5", - "resolved": "http://registry.npm.taobao.org/mocha/download/mocha-5.0.5.tgz", - "integrity": "sha1-4ijjOGuTh6RxAAemQfEnsAvkS1I=", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.11.0", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.3", - "he": "1.1.1", - "mkdirp": "0.5.1", - "supports-color": "4.4.0" - }, - "dependencies": { - "commander": { - "version": "2.11.0", - "resolved": "http://registry.npm.taobao.org/commander/download/commander-2.11.0.tgz", - "integrity": "sha1-FXFS/R56bI2YpbcVzzdt+SgARWM=", - "dev": true - } - } - }, "ms": { "version": "2.0.0", "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz", @@ -5596,11 +5446,6 @@ "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", "dev": true }, - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" - }, "node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -5689,7 +5534,8 @@ "object-assign": { "version": "4.1.1", "resolved": "http://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true }, "object-copy": { "version": "0.1.0", @@ -5722,15 +5568,11 @@ } } }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, "object-keys": { "version": "1.0.12", "resolved": "http://registry.npm.taobao.org/object-keys/download/object-keys-1.0.12.tgz", - "integrity": "sha1-CcU4VTd1dTEMymL1W7M0q/97PtI=" + "integrity": "sha1-CcU4VTd1dTEMymL1W7M0q/97PtI=", + "dev": true }, "object-visit": { "version": "1.0.1", @@ -5745,6 +5587,7 @@ "version": "4.1.0", "resolved": "http://registry.npm.taobao.org/object.assign/download/object.assign-4.1.0.tgz", "integrity": "sha1-lovxEA15Vrs8oIbwBvhGs7xACNo=", + "dev": true, "requires": { "define-properties": "^1.1.2", "function-bind": "^1.1.1", @@ -5764,68 +5607,6 @@ "has": "^1.0.1" } }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, "object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", @@ -5918,11 +5699,6 @@ "p-limit": "^1.1.0" } }, - "p-map": { - "version": "1.2.0", - "resolved": "http://registry.npm.taobao.org/p-map/download/p-map-1.2.0.tgz", - "integrity": "sha1-5OlPMR6rvIYzoeeZCBZfyiYkG2s=" - }, "p-try": { "version": "1.0.0", "resolved": "http://registry.npm.taobao.org/p-try/download/p-try-1.0.0.tgz", @@ -5967,7 +5743,8 @@ "path-is-inside": { "version": "1.0.2", "resolved": "http://registry.npm.taobao.org/path-is-inside/download/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true }, "path-key": { "version": "2.0.1", @@ -6009,20 +5786,17 @@ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, - "pify": { - "version": "3.0.0", - "resolved": "http://registry.npm.taobao.org/pify/download/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, "pinkie": { "version": "2.0.4", "resolved": "http://registry.npm.taobao.org/pinkie/download/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true }, "pinkie-promise": { "version": "2.0.1", "resolved": "http://registry.npm.taobao.org/pinkie-promise/download/pinkie-promise-2.0.1.tgz", "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, "requires": { "pinkie": "^2.0.0" } @@ -6430,6 +6204,7 @@ "version": "2.6.2", "resolved": "http://registry.npm.taobao.org/rimraf/download/rimraf-2.6.2.tgz", "integrity": "sha1-LtgVDSShbqhlHm1u8PR8QVjOejY=", + "dev": true, "requires": { "glob": "^7.0.5" } @@ -6461,7 +6236,8 @@ "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true }, "safe-regex": { "version": "1.1.0", @@ -6669,15 +6445,6 @@ } } }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, "shebang-command": { "version": "1.2.0", "resolved": "http://registry.npm.taobao.org/shebang-command/download/shebang-command-1.2.0.tgz", @@ -6710,60 +6477,6 @@ "dev": true, "optional": true }, - "should": { - "version": "13.2.1", - "resolved": "http://registry.npm.taobao.org/should/download/should-13.2.1.tgz", - "integrity": "sha1-hObr+7FFx54K5CMHsls/YtyvV04=", - "dev": true, - "requires": { - "should-equal": "^2.0.0", - "should-format": "^3.0.3", - "should-type": "^1.4.0", - "should-type-adaptors": "^1.0.1", - "should-util": "^1.0.0" - } - }, - "should-equal": { - "version": "2.0.0", - "resolved": "http://registry.npm.taobao.org/should-equal/download/should-equal-2.0.0.tgz", - "integrity": "sha1-YHLPgwRzYIZ+aOmLCdcRQ9BO4MM=", - "dev": true, - "requires": { - "should-type": "^1.4.0" - } - }, - "should-format": { - "version": "3.0.3", - "resolved": "http://registry.npm.taobao.org/should-format/download/should-format-3.0.3.tgz", - "integrity": "sha1-m/yPdPo5IFxT04w01xcwPidxJPE=", - "dev": true, - "requires": { - "should-type": "^1.3.0", - "should-type-adaptors": "^1.0.1" - } - }, - "should-type": { - "version": "1.4.0", - "resolved": "http://registry.npm.taobao.org/should-type/download/should-type-1.4.0.tgz", - "integrity": "sha1-B1bYzoRt/QmEOmlHcZ36DUz/XPM=", - "dev": true - }, - "should-type-adaptors": { - "version": "1.1.0", - "resolved": "http://registry.npm.taobao.org/should-type-adaptors/download/should-type-adaptors-1.1.0.tgz", - "integrity": "sha1-QB5/M7VTMDOUTVzYvytlAneS4no=", - "dev": true, - "requires": { - "should-type": "^1.3.0", - "should-util": "^1.0.0" - } - }, - "should-util": { - "version": "1.0.0", - "resolved": "http://registry.npm.taobao.org/should-util/download/should-util-1.0.0.tgz", - "integrity": "sha1-yYzaN0qmsZDfi6h8mInCtNtiAGM=", - "dev": true - }, "signal-exit": { "version": "3.0.2", "resolved": "http://registry.npm.taobao.org/signal-exit/download/signal-exit-3.0.2.tgz", @@ -7092,256 +6805,6 @@ "strip-ansi": "^4.0.0" } }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, "stringify-object-es5": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/stringify-object-es5/-/stringify-object-es5-2.5.0.tgz", @@ -7385,15 +6848,6 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, - "supports-color": { - "version": "4.4.0", - "resolved": "http://registry.npm.taobao.org/supports-color/download/supports-color-4.4.0.tgz", - "integrity": "sha1-iD992rwWUUKyphQn8zUt7RldGj4=", - "dev": true, - "requires": { - "has-flag": "^2.0.0" - } - }, "supports-hyperlinks": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", @@ -7767,70 +7221,6 @@ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", "dev": true }, - "util.promisify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz", - "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.2", - "has-symbols": "^1.0.1", - "object.getownpropertydescriptors": "^2.1.0" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, "uuid": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", diff --git a/package.json b/package.json index e27da6f..a11b2cc 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "gql-typescript-generator", - "version": "7.1.0", + "version": "9.0.5", "description": "", "main": "index.js", "bin": { "gqlg": "index.js" }, "scripts": { - "test": "mocha -u qunit", + "test": "jest", "lint": "./node_modules/.bin/eslint ./index.js" }, "repository": { @@ -19,7 +19,7 @@ "query", "generator" ], - "author": "timqian", + "author": "Lukasz Gandecki lukasz@xolv.io", "license": "MIT", "bugs": { "url": "https://github.com/TheBrainFamily/gql-typescript-generator/issues" @@ -27,7 +27,6 @@ "homepage": "https://github.com/TheBrainFamily/gql-typescript-generator#readme", "dependencies": { "commander": "^2.15.1", - "del": "^3.0.0", "find-package-json": "^1.2.0", "git-state": "^4.1.0", "graphql": "^0.13.2", @@ -40,9 +39,7 @@ "eslint-config-airbnb-base": "^13.1.0", "eslint-plugin-import": "^2.14.0", "jest": "^26.0.1", - "mocha": "^5.0.5", "prettier": "^2.0.5", - "should": "^13.2.1", "testdouble": "^3.15.0", "testdouble-jest": "^2.0.0" } diff --git a/parse-graphql/getFederatedEntities.js b/parse-graphql/getFederatedEntities.js new file mode 100644 index 0000000..90876cb --- /dev/null +++ b/parse-graphql/getFederatedEntities.js @@ -0,0 +1,14 @@ +const gql = require('graphql-tag'); +module.exports = (graphqlString) => { + const graphqlAST = gql` + ${graphqlString} + `; + + return graphqlAST.definitions + .filter((d) => ['Mutation', 'Query', 'Subscription'].indexOf(d.name.value) === -1) + .filter((d) => ['ObjectTypeDefinition'].indexOf(d.kind) > -1) + .filter((d) => { + return d.directives && d.directives.find((d) => d.name.value === 'key'); + }) + .map((f) => f.name.value); +}; diff --git a/parse-graphql/getFederatedEntities.spec.js b/parse-graphql/getFederatedEntities.spec.js new file mode 100644 index 0000000..a98c46e --- /dev/null +++ b/parse-graphql/getFederatedEntities.spec.js @@ -0,0 +1,25 @@ +const getExtendedTypes = require('./getFederatedEntities'); +const gql = (a) => a[0]; + +test.only('get the non extended types with apollo key annotation', () => { + const schemaString = gql` + type TodoItem @key(fields: "id") { + id: ID! + list: List + } + + extend type List { + id: ID! + todos: [TodoItem!]! + incompleteCount: Int! + } + + type InMemory { + id: ID! + } + `; + + const res = getExtendedTypes(schemaString); + + expect(res).toEqual(['TodoItem']); +}); diff --git a/parseGraphql/getModuleInfos.js b/parse-graphql/getModuleInfos.js similarity index 88% rename from parseGraphql/getModuleInfos.js rename to parse-graphql/getModuleInfos.js index 333ced1..d2459f2 100644 --- a/parseGraphql/getModuleInfos.js +++ b/parse-graphql/getModuleInfos.js @@ -4,6 +4,6 @@ module.exports = (namesObject) => { return namesObject.map((o) => { const schemaString = fs.readFileSync(o.graphqlFilePath, 'utf8'); const parsedGraphql = parseGraphql(schemaString); - return { name: o.name, ...parsedGraphql, types: parsedGraphql.typeDefinitions.length > 0 }; + return { name: o.name, ...parsedGraphql, types: parsedGraphql.typeDefinitions.length > 0, schemaString}; }); }; diff --git a/parseGraphql/getModuleInfos.spec.js b/parse-graphql/getModuleInfos.spec.js similarity index 90% rename from parseGraphql/getModuleInfos.spec.js rename to parse-graphql/getModuleInfos.spec.js index 2d775dc..e0c78fa 100644 --- a/parseGraphql/getModuleInfos.spec.js +++ b/parse-graphql/getModuleInfos.spec.js @@ -11,6 +11,6 @@ const exampleNames = [{ name: 'Accounts', graphqlFilePath: '/src/modules/Account test('', () => { const parsed = getModuleInfos(exampleNames)[0]; expect(parsed.name).toEqual('Accounts'); - expect(parsed.queries).toEqual([{ name: 'me' }]); + expect(parsed.queries).toMatchObject([{ name: 'me' }]); expect(parsed.types).toEqual(false); }); diff --git a/parseGraphql/getModuleNames.js b/parse-graphql/getModuleNames.js similarity index 100% rename from parseGraphql/getModuleNames.js rename to parse-graphql/getModuleNames.js diff --git a/parseGraphql/getModuleNames.spec.js b/parse-graphql/getModuleNames.spec.js similarity index 100% rename from parseGraphql/getModuleNames.spec.js rename to parse-graphql/getModuleNames.spec.js diff --git a/parseGraphql/parseGraphql.js b/parse-graphql/parseGraphql.js similarity index 57% rename from parseGraphql/parseGraphql.js rename to parse-graphql/parseGraphql.js index b3bc0e6..a124e7c 100644 --- a/parseGraphql/parseGraphql.js +++ b/parse-graphql/parseGraphql.js @@ -6,8 +6,20 @@ module.exports = (graphqlString) => { let foundQueries = graphqlAST.definitions.find((d) => d.name.value === 'Query'); let foundMutations = graphqlAST.definitions.find((d) => d.name.value === 'Mutation'); return { - queries: foundQueries && foundQueries.fields.map((f) => ({ name: f.name.value })), - mutations: foundMutations && foundMutations.fields.map((f) => ({ name: f.name.value })), + queries: + foundQueries && + foundQueries.fields.map((f) => ({ + name: f.name.value, + hasArguments: !!f.arguments.length, + variables: f.arguments.map((a) => a.name.value), + })), + mutations: + foundMutations && + foundMutations.fields.map((f) => ({ + name: f.name.value, + hasArguments: !!f.arguments.length, + variables: f.arguments.map((a) => a.name.value), + })), typeDefinitions: graphqlAST.definitions .filter((d) => ['Mutation', 'Query', 'Subscription'].indexOf(d.name.value) === -1) .filter((d) => ['ObjectTypeDefinition', 'ObjectTypeExtension'].indexOf(d.kind) > -1) diff --git a/parseGraphql/parseGraphql.spec.js b/parse-graphql/parseGraphql.spec.js similarity index 67% rename from parseGraphql/parseGraphql.spec.js rename to parse-graphql/parseGraphql.spec.js index 2993800..bedbe1a 100644 --- a/parseGraphql/parseGraphql.spec.js +++ b/parse-graphql/parseGraphql.spec.js @@ -32,7 +32,7 @@ const queryString = gql` test('should return names of the queries', () => { const res = parseGraphql(queryString); - expect(res.queries).toEqual([{ name: 'me' }, { name: 'notMe' }]); + expect(res.queries).toMatchObject([{ name: 'me' }, { name: 'notMe' }]); }); test('should return names of types', () => { @@ -42,13 +42,11 @@ test('should return names of types', () => { test('should return names of the Mutations', () => { const res = parseGraphql(queryString); - expect(res.mutations).toEqual([{ name: 'UserChangeName' }]); + expect(res.mutations).toMatchObject([{ name: 'UserChangeName' }]); }); - test('do not throw if queries not found', () => { const queryString = gql` - extend type Mutation { UserChangeName(name: String!): User! } @@ -73,8 +71,7 @@ test('do not throw if queries not found', () => { } `; parseGraphql(queryString); - -}) +}); test('do not throw if mutations not found', () => { const queryString = gql` @@ -82,7 +79,7 @@ test('do not throw if mutations not found', () => { me: User notMe: User } - + type User { id: ID! name: String @@ -104,8 +101,7 @@ test('do not throw if mutations not found', () => { `; parseGraphql(queryString); - -}) +}); test('do not throw if types not found', () => { const queryString = gql` @@ -113,12 +109,41 @@ test('do not throw if types not found', () => { me: User notMe: User } - + extend type Mutation { UserChangeName(name: String!): User! } + `; + + const res = parseGraphql(queryString); +}); +test('should return arguments list for query', () => { + const queryString = gql` + extend type Query { + UserChangeName(name: String!, age: Int): String! + NoArguments: String! + } `; + const res = parseGraphql(queryString); + expect(res.queries).toMatchObject([ + { name: 'UserChangeName', variables: ['name', 'age'] }, + { name: 'NoArguments', variables: [] }, + ]); +}); + +test('should return arguments list for mutation', () => { + const queryString = gql` + extend type Mutation { + UserChangeName(name: String!, age: Int): String! + NoArguments: String! + } + `; const res = parseGraphql(queryString); -}) + + expect(res.mutations).toMatchObject([ + { name: 'UserChangeName', variables: ['name', 'age'] }, + { name: 'NoArguments', variables: [] }, + ]); +}); diff --git a/scaffold/.eslintrc b/scaffold/.eslintrc new file mode 100644 index 0000000..50f173e --- /dev/null +++ b/scaffold/.eslintrc @@ -0,0 +1,32 @@ +{ + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "./tsconfig.json" + }, + "extends": [ + "airbnb-typescript/base", + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "prettier/@typescript-eslint", + "plugin:prettier/recommended" + ], + "plugins": ["@typescript-eslint", "prettier"], + "rules": { + "import/no-extraneous-dependencies": [ + "error", + { "devDependencies": ["**/*.test.ts", "**/*.spec.ts"] } + ], + "prettier/prettier": "error", + "import/prefer-default-export": 0, + "no-underscore-dangle": 0, + "no-cond-assign": ["error", "except-parens"], + "import/no-cycle": 0, + "@typescript-eslint/interface-name-prefix": 0, + "@typescript-eslint/camelcase": ["error",{"allow": ["resolveReference"]}] + }, + "env": { + "browser": true, + "jest": true + } +} diff --git a/scaffold/.gitignore b/scaffold/.gitignore new file mode 100644 index 0000000..d5dfb56 --- /dev/null +++ b/scaffold/.gitignore @@ -0,0 +1,9 @@ +.idea +.eslintcache +node_modules +coverage +lib/ +dist/ +.cache +generated/graphql +schema.graphql diff --git a/scaffold/README.md b/scaffold/README.md new file mode 100644 index 0000000..736708c --- /dev/null +++ b/scaffold/README.md @@ -0,0 +1,64 @@ +## Setup + +`npm install` + + +## Development + +Start the backend in dev (watch) mode: + +`npm start` + +To run tests: + +`npm test` + +To run tests in watch mode: + +`npm run test:watch` + +To run type check: + +`npm run type-check` + +To run lint: + +`npm run lint` + +## Type Generation / Workflow + +Your schema files have to be in a structure of `./src/modules/MODULE_NAME/graphql/MODULE_NAME.graphql`, for example `./src/modules/Lists/graphql/Lists.graphql`. + +Anytime you modify one of your graphql files remember to run `npm run graphql:generateAll`. + +It will create Mutations/Queries/Type resolvers, tests for them, types and perform all the necessary connection between the main schema, contexts, etc. +It's advisable to create a new module, fill it with schema, run the generation to see what are the resulting changes. +Remember to start with a clean, commited state. It's difficult to compare and verify the generation results if you've had changes in the code already, so that's blocked by default. +The tooling will only allow you to run the generation if there are no changes, or if .graphql files are the only one changed. + +Let's assume we've created a new module named Users: + +`mkdir -p src/modules/Users/graphql` + +And now create a simple schema file for it `src/modules/Users/graphql/Users.graphql`: + +```graphql +type User { + id: ID! + name: String +} + +extend type Query { + UserById(id: ID!): User! +} + +extend type Mutation { + UserAdd(name: String!): User! +} +``` + +> Please note, we extend Queries and Mutations, as those are defined something else - that helps IDEs to understand that we don't have conflicting types defined in our project + +Let's run the generation now: + +`npm run graphql:generateAll` diff --git a/scaffold/codegen.js b/scaffold/codegen.js new file mode 100644 index 0000000..612e6d2 --- /dev/null +++ b/scaffold/codegen.js @@ -0,0 +1,72 @@ +const fs = require("fs"); +const { importSchema } = require("graphql-import"); +const { isObjectType } = require("graphql"); + +let schemaString = fs + .readFileSync("./schema.graphql") + .toString() + .replace(/extend type/g, `type`); + +schemaString = `${schemaString} + +## Federation Types + +scalar _FieldSet + +directive @external on FIELD_DEFINITION +directive @requires(fields: _FieldSet!) on FIELD_DEFINITION +directive @provides(fields: _FieldSet!) on FIELD_DEFINITION +directive @key(fields: _FieldSet!) on OBJECT | INTERFACE + +directive @extends on OBJECT +` + .replace( + "@entity(embedded: Boolean)", + "@entity(embedded: Boolean, additionalFields: [AdditionalEntityFields])" + ) + .replace( + "@union(discriminatorField: String)", + "@union(discriminatorField: String, additionalFields: [AdditionalEntityFields])" + ); + +const schema = importSchema(schemaString, {}, { out: "GraphQLSchema" }); +const typeMap = schema.getTypeMap(); + +const mappers = {}; +for (const typeName in typeMap) { + const type = schema.getType(typeName); + if (isObjectType(type)) { + if (type.toConfig().astNode) { + if ( + type + .toConfig() + .astNode.directives.find((d) => d.name.value === "entity") + ) { + mappers[typeName] = `${typeName}DbObject`; + } + } + } +} + +module.exports = { + overwrite: true, + schema: schemaString, + generates: { + "generated/graphql/types.ts": { + config: { + contextType: "@app/context#GqlContext", + idFieldName: "id", + objectIdType: "string", + federation: true, + mappers, + }, + plugins: [ + "typescript", + "typescript-resolvers", + "typescript-operations", + "typescript-mongodb", + { add: "export {GqlContext};" }, + ], + }, + }, +}; diff --git a/scaffold/fix-generated.js b/scaffold/fix-generated.js new file mode 100644 index 0000000..afaa2e6 --- /dev/null +++ b/scaffold/fix-generated.js @@ -0,0 +1,8 @@ +const shell = require("shelljs"); + +shell.sed( + "-i", + /\| StitchingResolver;/, + "", + "./generated/graphql/types.ts" +); diff --git a/scaffold/jest.config.js b/scaffold/jest.config.js new file mode 100644 index 0000000..9f08989 --- /dev/null +++ b/scaffold/jest.config.js @@ -0,0 +1,13 @@ +const { pathsToModuleNameMapper } = require("ts-jest/utils"); +const { compilerOptions } = require("./tsconfig"); + +module.exports = { + preset: "ts-jest", + testEnvironment: "node", + testRunner: "jest-circus/runner", + setupFiles: ["./jest.setup.js"], + testPathIgnorePatterns: ["/node_modules/", "/.yalc/"], + moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { + prefix: "/", + }), +}; diff --git a/scaffold/jest.setup.js b/scaffold/jest.setup.js new file mode 100644 index 0000000..e4aba47 --- /dev/null +++ b/scaffold/jest.setup.js @@ -0,0 +1,4 @@ +const td = require('testdouble') +require('testdouble-jest')(td, jest) + +process.env.TEST_ENV = "true" diff --git a/scaffold/nodemon.run.json b/scaffold/nodemon.run.json new file mode 100644 index 0000000..30d588d --- /dev/null +++ b/scaffold/nodemon.run.json @@ -0,0 +1,11 @@ +{ + "delay": 1500, + "watch": [ + "src" + ], + "ignore": [ + "src/**/*.spec.ts", + "src/**/*.test.ts" + ], + "ext": "ts,graphql" +} diff --git a/scaffold/package.json b/scaffold/package.json new file mode 100644 index 0000000..ce5c148 --- /dev/null +++ b/scaffold/package.json @@ -0,0 +1,80 @@ +{ + "name": "graphql-todo-lists", + "version": "1.0.0", + "description": "Small Standalone Scaffold", + "license": "ISC", + "scripts": { + "graphql:codegen": "gqlg --codeGen", + "graphql:generateAll": "npm run graphql:codegen && npm run graphql:generateSchema && npm run graphql:typegen && npm run prettify:all", + "graphql:generateSchema": "ts-node -r tsconfig-paths/register ./generated/graphql/printSchema.ts > ./schema.graphql", + "graphql:typegen": "graphql-codegen --config codegen.js && node ./fix-generated", + "postinstall": "npm run graphql:generateAll", + "lint": "eslint --fix --ext .ts --quiet --cache src/", + "precommit": "lint-staged", + "prettify:all": "npx prettier --write \"src/**/*.ts\" \"generated/**/*.ts\" --loglevel error", + "start": "npm run start:ts", + "start:ts": "nodemon -r tsconfig-paths/register --config ./nodemon.run.json ./src/index.ts", + "test": "jest --forceExit", + "test:watch": "jest --watch", + "type-check": "tsc --noEmit", + "type-check:watch": "npm run type-check -- --watch" + }, + "lint-staged": { + "src/**/*.ts*": [ + "eslint --fix", + "jest --findRelatedTests --forceExit" + ], + "package.json": [ + "sort-package-json" + ] + }, + "dependencies": { + "@apollo/federation": "0.17.0", + "@graphql-tools/graphql-file-loader": "6.0.14", + "@graphql-tools/load": "6.0.14", + "@graphql-tools/merge": "6.0.14", + "apollo-server-express": "2.15.1", + "cache-manager": "3.3.0", + "cookie-parser": "1.4.5", + "express": "4.17.1", + "graphql": "15.3.0", + "jsonwebtoken": "8.5.1", + "lodash": "4.17.19", + "portable-fetch": "3.0.0", + "shelljs": "0.8.4", + "tsconfig-paths": "3.9.0" + }, + "devDependencies": { + "@graphql-codegen/add": "1.17.0", + "@graphql-codegen/cli": "1.17.0", + "@graphql-codegen/typescript": "1.17.0", + "@graphql-codegen/typescript-mongodb": "1.17.0", + "@graphql-codegen/typescript-operations": "1.17.0", + "@graphql-codegen/typescript-resolvers": "1.17.0", + "@jest/globals": "26.1.0", + "@types/jest": "26.0.4", + "@types/node": "14.0.23", + "@types/shelljs": "0.8.8", + "@typescript-eslint/eslint-plugin": "3.6.1", + "@typescript-eslint/parser": "3.6.1", + "eslint": "7.4.0", + "eslint-config-airbnb-typescript": "8.0.2", + "eslint-config-prettier": "6.11.0", + "eslint-plugin-import": "2.22.0", + "eslint-plugin-prettier": "3.1.4", + "gql-typescript-generator": "latest", + "graphql-import": "1.0.2", + "jest": "26.1.0", + "jest-circus": "26.1.0", + "kill-port": "1.6.1", + "lint-staged": "10.2.11", + "nodemon": "2.0.4", + "prettier": "2.0.5", + "sort-package-json": "1.44.0", + "testdouble": "3.16.1", + "testdouble-jest": "2.0.0", + "ts-jest": "26.1.3", + "ts-node": "8.10.2", + "typescript": "3.9.7" + } +} diff --git a/scaffold/src/context.ts b/scaffold/src/context.ts new file mode 100644 index 0000000..0cc1f42 --- /dev/null +++ b/scaffold/src/context.ts @@ -0,0 +1,21 @@ +import express from "express"; +import { RootInterface } from "./root"; + +export type GqlContext = RootInterface & { + headers: { + [key: string]: string | string[]; + }; + jwt?: string; +}; + +export const appContext = (root: RootInterface) => ({ + req, +}: { + req: express.Request; +}): GqlContext => { + return { + ...root, + headers: req.headers, + jwt: req.cookies.jwt, + }; +}; diff --git a/scaffold/src/createApp.ts b/scaffold/src/createApp.ts new file mode 100644 index 0000000..cb54a16 --- /dev/null +++ b/scaffold/src/createApp.ts @@ -0,0 +1,40 @@ +import { ApolloServer } from "apollo-server-express"; +import express from "express"; +import cookieParser from "cookie-parser"; + +import { appContext } from "./context"; +import { schema } from "./graphql/schema"; +import { root } from "./root"; + +// We are keeping async here because we want to be able to await operations without changing the API of createApp. +export const createApp = async () => { + const app = express(); + + app.use([cookieParser()]); + + // FIXES CORS ERROR - + // so if you need to block other domains, you have an example + const whitelist = [ + "http://localhost:3000", + "http://localhost:4000", + "http://xolv.io", + "https://xolv.io", + ]; + + const corsOptions = { + origin(origin, callback) { + const originIsWhitelisted = whitelist.indexOf(origin) !== -1 || !origin; + callback(null, originIsWhitelisted); + }, + credentials: true, + }; + + const apollo = new ApolloServer({ + schema, + context: appContext(root), + }); + + apollo.applyMiddleware({ app, cors: corsOptions }); + + return { app }; +}; diff --git a/scaffold/src/graphql/schema.graphql.skip b/scaffold/src/graphql/schema.graphql.skip new file mode 100644 index 0000000..12777f6 --- /dev/null +++ b/scaffold/src/graphql/schema.graphql.skip @@ -0,0 +1,15 @@ +## If you need to add extra generic directives here, change this file name to schema.graphql. + +# some common examples of directives: + +#directive @adminOnly on OBJECT | FIELD_DEFINITION +#directive @authorized(requires: [Roles]) on OBJECT | FIELD_DEFINITION + +#enum Roles { +# ADMIN +# REGISTERED_USER +#} + +type Status { + status: String! +} diff --git a/scaffold/src/graphql/schema.ts b/scaffold/src/graphql/schema.ts new file mode 100644 index 0000000..cd50a35 --- /dev/null +++ b/scaffold/src/graphql/schema.ts @@ -0,0 +1,12 @@ +import { buildFederatedSchema } from "@apollo/federation"; +import { resolvers } from "@generated/graphql/resolvers"; +import typeDefs from "@generated/graphql/combineSchemas"; + +const schema = buildFederatedSchema([ + { + typeDefs, + resolvers, + }, +]); + +export { schema }; diff --git a/scaffold/src/index.ts b/scaffold/src/index.ts new file mode 100644 index 0000000..74fa47c --- /dev/null +++ b/scaffold/src/index.ts @@ -0,0 +1,25 @@ +import { AddressInfo } from "net"; +import { createApp } from "./createApp"; + +const opts = { + port: process.env.PORT || 4002, +}; + +const getUrl = (address, port) => + `://${address === "::" ? "localhost" : address}:${port}`; + +createApp() + .then(({ app }) => { + const server = app.listen(opts.port); + const { port, address } = server.address() as AddressInfo; + process.once("SIGTERM", function () { + server.close(function () { + process.kill(process.pid, "SIGTERM"); + }); + }); + console.log(`Server listening on ${getUrl(address, port)}`); + console.log(`GraphQL playground at http://${getUrl(address, port)}/graphql`); + }) + .catch((e) => { + console.log("Server did not start up with this error", e); + }); diff --git a/scaffold/src/modules/RemoveMe/graphql/RemoveMe.graphql b/scaffold/src/modules/RemoveMe/graphql/RemoveMe.graphql new file mode 100644 index 0000000..a358d6d --- /dev/null +++ b/scaffold/src/modules/RemoveMe/graphql/RemoveMe.graphql @@ -0,0 +1,3 @@ +extend type Query { + Hello(greeting: String!): String +} diff --git a/scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.spec.ts b/scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.spec.ts new file mode 100644 index 0000000..1ece12a --- /dev/null +++ b/scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.spec.ts @@ -0,0 +1,16 @@ +import td from "testdouble"; +import { GqlContext, QueryHelloArgs } from "@generated/graphql/types"; +import { HelloQuery } from "./HelloQuery"; + +const testHello = (variables: QueryHelloArgs, context: GqlContext) => + HelloQuery({}, variables, context, null); + +test("Hello", async () => { + const context = td.object(); + + const variables: QueryHelloArgs = { greeting: "Hello there!" }; + + const result = await testHello(variables, context); + + expect(result).toEqual("Hello there!"); +}); diff --git a/scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.ts b/scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.ts new file mode 100644 index 0000000..f77bcd6 --- /dev/null +++ b/scaffold/src/modules/RemoveMe/graphql/queries/HelloQuery.ts @@ -0,0 +1,5 @@ +import { QueryResolvers } from "@generated/graphql/types"; + +export const HelloQuery: QueryResolvers["Hello"] = (parent, args, context) => { + return args.greeting; +}; diff --git a/scaffold/src/root.ts b/scaffold/src/root.ts new file mode 100644 index 0000000..137215d --- /dev/null +++ b/scaffold/src/root.ts @@ -0,0 +1,6 @@ +// Initialize your Controllers / Data Sources / Repositories here. +// Thie shape of this object will also be extended by your context.ts file to define a Gql Context + +export const root = {}; + +export type RootInterface = typeof root; diff --git a/scaffold/tsconfig.json b/scaffold/tsconfig.json new file mode 100644 index 0000000..870cdfe --- /dev/null +++ b/scaffold/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "module": "commonjs", + "target": "es2019", + "sourceMap": true, + "outDir": "./lib/", + "esModuleInterop": true, + "baseUrl": "./", + "paths": { + "@app/*": ["./src/*"], + "@generated/*": ["./generated/*"] + } + }, + "exclude": [ + "./node_modules" + ] +} diff --git a/templates/IModuleNameContext.handlebars b/templates/IModuleNameContext.handlebars deleted file mode 100644 index 379cd78..0000000 --- a/templates/IModuleNameContext.handlebars +++ /dev/null @@ -1,3 +0,0 @@ -export interface I{{moduleName}}Context { -{{moduleName}}: {}; -} diff --git a/templates/combineSchemas.ts b/templates/combineSchemas.ts new file mode 100644 index 0000000..733aa10 --- /dev/null +++ b/templates/combineSchemas.ts @@ -0,0 +1,44 @@ +import * as fs from "fs"; +import { loadTypedefsSync } from "@graphql-tools/load"; +import { + GraphQLFileLoader, + GraphQLFileLoaderOptions, +} from "@graphql-tools/graphql-file-loader"; +import { mergeTypeDefs } from "@graphql-tools/merge"; + +import shelljs from "shelljs"; + +const graphqlPaths = shelljs.ls( + `${__dirname}/../../src/modules/*/graphql/*.graphql` +); + +let allSchemas = graphqlPaths + .concat(`${__dirname}/genericDataModelSchema.graphql`) + +const schemaExtensionPath = `${__dirname}/../../src/graphql/schema.graphql` +if (fs.existsSync(schemaExtensionPath)) { + allSchemas = allSchemas.concat(schemaExtensionPath) +} + +class ExtendedGraphQLFileLoader extends GraphQLFileLoader { + handleFileContent( + rawSDL: string, + pointer: string, + options: GraphQLFileLoaderOptions + ) { + const newSdl = rawSDL + .replace("extend type Query", "type Query") + .replace("extend type Mutation", "type Mutation"); + // .replace(/extend type/g, "type"); + return super.handleFileContent(newSdl, pointer, options); + } +} + +const schema = mergeTypeDefs( + loadTypedefsSync(allSchemas, { + loaders: [new ExtendedGraphQLFileLoader()], + assumeValidSDL: true, // this will bypass validation + }).map((s) => s.document) +); + +export default schema; diff --git a/templates/context.handlebars b/templates/context.handlebars new file mode 100644 index 0000000..f5f0d88 --- /dev/null +++ b/templates/context.handlebars @@ -0,0 +1,21 @@ +import express from "express"; +import { RootInterface } from "./root"; + +export type GqlContext = RootInterface & { + headers: { + [key: string]: string | string[]; + }; + // jwt?: string; +}; + +export const appContext = (root: RootInterface) => ({ + req, +}: { + req: express.Request; +}): GqlContext => { + return { + ...root, + headers: req.headers, + // jwt: req.cookies.jwt, + }; +}; diff --git a/templates/frameworkSchema.graphql b/templates/frameworkSchema.graphql new file mode 100644 index 0000000..bc1175e --- /dev/null +++ b/templates/frameworkSchema.graphql @@ -0,0 +1,3 @@ +## GraphQL Template +type Query +type Mutation diff --git a/templates/genericDataModelSchema.graphql b/templates/genericDataModelSchema.graphql new file mode 100644 index 0000000..975443e --- /dev/null +++ b/templates/genericDataModelSchema.graphql @@ -0,0 +1,14 @@ +# Mongo types +directive @entity(embedded: Boolean) on OBJECT + +directive @column(overrideType: String) on FIELD_DEFINITION + +directive @id on FIELD_DEFINITION +directive @link(overrideType: String) on FIELD_DEFINITION +directive @embedded on FIELD_DEFINITION +directive @union(discriminatorField: String) on UNION + +input AdditionalEntityFields { + path: String + type: String +} diff --git a/templates/getModuleNameContext.handlebars b/templates/getModuleNameContext.handlebars deleted file mode 100644 index 5dca8f1..0000000 --- a/templates/getModuleNameContext.handlebars +++ /dev/null @@ -1,8 +0,0 @@ -// Warning, this file is autogenerated, please don't change it manually. -// Your changes will get overwritten. - -import { I{{moduleName}}Context } from "./I{{moduleName}}Context"; - -export const get{{moduleName}}Context = (): I{{moduleName}}Context => ({ -{{moduleName}}: {} -}); diff --git a/templates/module.graphql b/templates/module.graphql deleted file mode 100644 index 2b0cbf4..0000000 --- a/templates/module.graphql +++ /dev/null @@ -1,3 +0,0 @@ -# extend type Query {} -# extend type Mutation {} - diff --git a/templates/moduleResolvers.handlebars b/templates/moduleResolvers.handlebars index 96373c6..e7affaf 100644 --- a/templates/moduleResolvers.handlebars +++ b/templates/moduleResolvers.handlebars @@ -1,12 +1,14 @@ -{{#each queries}}import { {{name}}Query } from "./queries/{{name}}Query";{{/each}} -{{#each mutations}}import { {{name}}Mutation } from "./mutations/{{name}}Mutation";{{/each}} -{{#if types}}import { typeResolvers } from "./types/typeResolvers";{{/if}} +import {Resolvers} from "./types"; +{{#each queries}}import { {{name}}Query } from "@app/modules/{{../moduleName}}/graphql/queries/{{name}}Query";{{/each}} +{{#each mutations}}import { {{name}}Mutation } from "@app/modules/{{../moduleName}}/graphql/mutations/{{name}}Mutation";{{/each}} +{{#each typeResolvers}}{{#each fieldName}}import { {{../typeName}}{{capitalizedName}} } from "@app/modules/{{../../moduleName}}/graphql/types/{{../typeName}}{{capitalizedName}}";{{/each}}{{/each}} // Warning, this file is autogenerated, please don't change it manually. // Your changes will get overwritten. -export const {{moduleName}}Resolvers = { -{{#if types}}...typeResolvers,{{/if}} -{{#if queries.length}}Query: { {{#each queries}}...{{name}}Query,{{/each}} },{{/if}} -{{#if mutations.length}}Mutation: { {{#each mutations}}...{{name}}Mutation,{{/each}} },{{/if}} +export const {{moduleName}}Resolvers: Resolvers = { +{{#each typeResolvers}} + {{typeName}}: { {{#each fieldName}}{{name}}: {{../typeName}}{{capitalizedName}},{{/each}} }, {{/each}} +{{#if queries.length}}Query: { {{#each queries}}{{name}}: {{name}}Query,{{/each}} },{{/if}} +{{#if mutations.length}}Mutation: { {{#each mutations}}{{name}}: {{name}}Mutation,{{/each}} },{{/if}} }; diff --git a/templates/mutation.handlebars b/templates/mutation.handlebars index 3a6a4bc..cf07e9c 100644 --- a/templates/mutation.handlebars +++ b/templates/mutation.handlebars @@ -1,8 +1,3 @@ -import { MutationResolvers } from "../../../../graphql/generated"; +import { MutationResolvers } from "@generated/graphql/types"; -export const {{mutationName}}Mutation: MutationResolvers = { -{{mutationName}}: (parent, args, context) => { - -throw new Error("{{mutationName}} resolver not implemented yet"); -}, -}; +export const {{mutationName}}Mutation: MutationResolvers["{{mutationName}}"] = (parent, args, context) => { throw new Error("not implemented yet"); } diff --git a/templates/mutation.spec.handlebars b/templates/mutation.spec.handlebars index 9eb3865..bae3b60 100644 --- a/templates/mutation.spec.handlebars +++ b/templates/mutation.spec.handlebars @@ -1,10 +1,20 @@ -// import td from "testdouble"; +import td from "testdouble"; +import { GqlContext, {{#if hasArguments}}Mutation{{toUpperCase mutationName}}Args{{/if}} } from "@generated/graphql/types"; +import { {{mutationName}}Mutation } from "./{{mutationName}}Mutation"; -import { getGqlClientWithMockedContext } from "../../../../graphql/testHelpers/getGqlClientWithMockedContext"; +const test{{mutationName}} = ({{#if hasArguments}}variables: Mutation{{toUpperCase mutationName}}Args, {{/if}}context: GqlContext) => {{mutationName}}Mutation({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) -test("{{mutationName}} Mutation passes and gets the data from the module usecase", async () => { -const { mutations, context } = getGqlClientWithMockedContext(); -// td.when(context.{{moduleName}}.contextAction()).thenResolve(); -const result = await mutations.{{mutationName}}(); -}); +test("{{mutationName}}", async () => { + +const context = td.object(); + + // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() + + {{#if hasArguments}} + const variables: Mutation{{toUpperCase mutationName}}Args = {} + {{/if}} + + const result = await test{{mutationName}}({{#if hasArguments}}variables,{{/if}} context); + + }); diff --git a/templates/printSchema.ts b/templates/printSchema.ts new file mode 100644 index 0000000..a090f56 --- /dev/null +++ b/templates/printSchema.ts @@ -0,0 +1,4 @@ +import { print } from "graphql"; +import schema from "./combineSchemas"; + +console.log(print(schema)); diff --git a/templates/query.handlebars b/templates/query.handlebars index b635971..efdd4e8 100644 --- a/templates/query.handlebars +++ b/templates/query.handlebars @@ -1,8 +1,4 @@ -import { QueryResolvers } from "../../../../graphql/generated"; +import { QueryResolvers } from "@generated/graphql/types"; -export const {{queryName}}Query: QueryResolvers = { - {{queryName}}: (parent, args, context) => { +export const {{queryName}}Query: QueryResolvers["{{queryName}}"] = (parent, args, context) => { throw new Error("not implemented yet"); } - throw new Error("{{queryName}} resolver not implemented yet"); -}, -}; diff --git a/templates/query.spec.handlebars b/templates/query.spec.handlebars index 1590416..ad7cd92 100644 --- a/templates/query.spec.handlebars +++ b/templates/query.spec.handlebars @@ -1,10 +1,21 @@ -// import td from "testdouble"; +import td from "testdouble"; +import { GqlContext, {{#if hasArguments}}Query{{toUpperCase queryName}}Args{{/if}} } from "@generated/graphql/types"; +import { {{queryName}}Query } from "./{{queryName}}Query"; -import { getGqlClientWithMockedContext } from "../../../../graphql/testHelpers/getGqlClientWithMockedContext"; -test("{{queryName}} Query passes and gets the data from the module usecase", async () => { -const { queries, context } = getGqlClientWithMockedContext(); +const test{{queryName}} = ({{#if hasArguments}}variables: Query{{toUpperCase queryName}}Args,{{/if}} context: GqlContext) => {{queryName}}Query({}, {{#if hasArguments}}variables{{else}} {} {{/if}}, context, null) + + +test("{{queryName}}", async () => { + + const context = td.object(); + + // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() + + {{#if hasArguments}} + const variables: Query{{toUpperCase queryName}}Args = {} + {{/if}} + + const result = await test{{queryName}}({{#if hasArguments}}variables,{{/if}} context); -// td.when(context.{{moduleName}}.contextAction()).thenResolve(); -const result = await queries.{{queryName}}(); }); diff --git a/templates/resolvers.handlebars b/templates/resolvers.handlebars index 7a0569c..3612e70 100644 --- a/templates/resolvers.handlebars +++ b/templates/resolvers.handlebars @@ -1,5 +1,5 @@ import merge from "lodash/merge"; -{{#each modules}}import { {{name}}Resolvers } from "../modules/{{name}}/graphql/{{name}}Resolvers";{{/each}} +{{#each modules}}import { {{name}}Resolvers } from "./{{name}}Resolvers";{{/each}} // This file is generated. If you want to add custom resolvers do following steps: // 1. create customResolvers.ts file diff --git a/templates/root.handlebars b/templates/root.handlebars new file mode 100644 index 0000000..898a726 --- /dev/null +++ b/templates/root.handlebars @@ -0,0 +1,8 @@ +// Initialize your Controllers / Data Sources / Repositories here. +// Thie shape of this object will also be extended by your context.ts file to define a Gql Context + +export const root = { + // todoItemController: new TodoItemControllerCacheable(), +}; + +export type RootInterface = typeof root; diff --git a/templates/startupConfig.handlebars b/templates/startupConfig.handlebars deleted file mode 100644 index a45b2dc..0000000 --- a/templates/startupConfig.handlebars +++ /dev/null @@ -1,12 +0,0 @@ -// Warning, this file is autogenerated, please don't change it manually. -// Your changes will get overwritten. - -import { MyContext } from "./types"; -import { schema } from "./graphql/schema"; - -{{#each modules}}import { get{{name}}Context } from "./modules/{{name}}/get{{name}}Context";{{/each}} - -const context = (): MyContext => ({ -{{#each modules}}...get{{name}}Context(),{{/each}} -}); -export const startupConfig = { schema, context }; diff --git a/templates/typeResolvers.handlebars b/templates/typeResolvers.handlebars deleted file mode 100644 index 4d0e45b..0000000 --- a/templates/typeResolvers.handlebars +++ /dev/null @@ -1,7 +0,0 @@ -{{#each type}}import { {{name}}TypeResolvers } from "./{{name}}TypeResolvers";{{/each}} - -export const typeResolvers = { -{{#each type}} -{{name}}: {{name}}TypeResolvers, -{{/each}} -}; diff --git a/templates/typeTypeResolvers.handlebars b/templates/typeTypeResolvers.handlebars index f39ff5a..65491a7 100644 --- a/templates/typeTypeResolvers.handlebars +++ b/templates/typeTypeResolvers.handlebars @@ -1,5 +1,3 @@ -import { {{typeName}}Resolvers } from "../../../../graphql/generated"; +import { {{typeName}}Resolvers } from "@generated/graphql/types"; -export const {{typeName}}TypeResolvers = { - -} as {{typeName}}Resolvers; +export const {{typeName}}{{capitalizedFieldName}}: {{typeName}}Resolvers["{{fieldName}}"] = (parent, {{#unless resolveReferenceType}}args,{{/unless}} context) => { throw new Error("Not implemented yet"); } diff --git a/templates/typeTypeResolvers.spec.handlebars b/templates/typeTypeResolvers.spec.handlebars new file mode 100644 index 0000000..95abb55 --- /dev/null +++ b/templates/typeTypeResolvers.spec.handlebars @@ -0,0 +1,24 @@ +import td from "testdouble"; + +import { GqlContext, ResolversParentTypes, {{#if hasArguments}}{{typeName}}{{capitalizedFieldName}}Args{{/if}} } from "@generated/graphql/types"; +import { {{typeName}}{{capitalizedFieldName}} } from "./{{typeName}}{{capitalizedFieldName}}"; + +{{#if resolveReferenceType}}type ParentType = Parameters[0];{{/if}} + + const test{{typeName}}{{capitalizedFieldName}} = (parent: {{#if resolveReferenceType}}ParentType{{else}}ResolversParentTypes["{{typeName}}"]{{/if}}, {{#if hasArguments}}variables: {{typeName}}{{capitalizedFieldName}}Args,{{/if}} context: GqlContext) => {{typeName}}{{capitalizedFieldName}}(parent, {{#unless resolveReferenceType}}{{#if hasArguments}}variables{{else}} {} {{/if}},{{/unless}} context, null) + + +test("{{typeName}}{{capitalizedFieldName}}", async () => { + +const context = td.object(); + + // td.when(context.{{moduleName}}Repository.findOne()).thenResolve() + + const parent{{#if resolveReferenceType}} = {} as ParentType {{else}}: ResolversParentTypes["{{typeName}}"] = {}{{/if}} + {{#if hasArguments}} + const variables: {{typeName}}{{capitalizedFieldName}}Args = {} + {{/if}} + + const result = await test{{typeName}}{{capitalizedFieldName}}(parent, {{#if hasArguments}}variables,{{/if}} context); + + }); diff --git a/templates/types.handlebars b/templates/types.handlebars deleted file mode 100644 index 2ffdbdc..0000000 --- a/templates/types.handlebars +++ /dev/null @@ -1,6 +0,0 @@ -/* eslint-disable import/no-cycle */ -// cycles are ok here since we only deal with types. -{{#each modules}}import { I{{name}}Context } from "./modules/{{name}}/I{{name}}Context"; -{{/each}} - -export interface MyContext extends {{#each modules}}I{{name}}Context,{{/each}} {}