Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type resolvers in modules #6

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,5 @@
"no-console": "off",
"global-require": "off",
"no-plusplus": "off"
},
"env": {
"mocha": true
}
}

250 changes: 167 additions & 83 deletions generateModule.js
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -78,97 +121,138 @@ 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);
});
}
});

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();

2 changes: 1 addition & 1 deletion helpers/checkIfGitStateClean.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions helpers/saveRenderedTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down
Loading