ModelRenderer.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import Mustache from 'mustache';
  2. import { Field, ObjectType } from '../model';
  3. import * as path from 'path';
  4. import { kebabCase, camelCase } from 'lodash';
  5. import { getTypesForArray, names, pascalCase, camelPlural } from './utils';
  6. import Debug from "debug";
  7. import { GeneratorContext } from './SourcesGenerator';
  8. const debug = Debug('qnode-cli:model-renderer');
  9. const TYPE_FIELDS: { [key: string]: { [key: string]: string } } = {
  10. bool: {
  11. decorator: 'BooleanField',
  12. tsType: 'boolean'
  13. },
  14. date: {
  15. decorator: 'DateField',
  16. tsType: 'Date'
  17. },
  18. int: {
  19. decorator: 'IntField',
  20. tsType: 'number'
  21. },
  22. float: {
  23. decorator: 'FloatField',
  24. tsType: 'number'
  25. },
  26. json: {
  27. decorator: 'JSONField',
  28. tsType: 'JsonObject'
  29. },
  30. otm: {
  31. decorator: 'OneToMany',
  32. tsType: '---'
  33. },
  34. string: {
  35. decorator: 'StringField',
  36. tsType: 'string'
  37. },
  38. numeric: {
  39. decorator: 'NumericField',
  40. tsType: 'string'
  41. },
  42. decimal: {
  43. decorator: 'NumericField',
  44. tsType: 'string'
  45. },
  46. oto: {
  47. decorator: 'OneToOne',
  48. tsType: '---'
  49. },
  50. array: {
  51. decorator: 'ArrayField',
  52. tsType: '' // will be updated with the correct type
  53. },
  54. bytes: {
  55. decorator: 'BytesField',
  56. tsType: 'Buffer'
  57. }
  58. };
  59. type FunctionProp = () => string;
  60. type MustacheProp = string | FunctionProp;
  61. export interface MustacheObjectType {
  62. generatedFolderRelPath: string,
  63. className: string
  64. fields: MustacheField[],
  65. has: Props // hasBooleanField, hasIntField, ...
  66. }
  67. export interface MustacheField {
  68. camelName: MustacheProp,
  69. tsType: MustacheProp,
  70. decorator: MustacheProp,
  71. relClassName?: string,
  72. relCamelName?: string,
  73. relPathForModel?: string,
  74. apiType?: string,
  75. dbType?: string,
  76. required: boolean,
  77. is: Props // isOtm, isMto, isScalar ...
  78. }
  79. interface Props {
  80. [key: string]: boolean | string
  81. }
  82. export class ModelRenderer {
  83. private context: GeneratorContext = {};
  84. constructor(context: GeneratorContext = {}) {
  85. this.context = context;
  86. }
  87. transformField(f: Field, entity: ObjectType): MustacheField {
  88. let ret = {};
  89. const isProps: Props = {};
  90. isProps['array'] = f.isArray();
  91. isProps['scalar'] = f.isScalar();
  92. ['mto', 'oto', 'otm'].map((s) => isProps[s] = (f.type === s));
  93. isProps['refType'] = isProps['mto'] || isProps['oto'] || isProps['otm'];
  94. const fieldType = f.columnType();
  95. ret = {
  96. is: isProps,
  97. required: !f.nullable,
  98. ...TYPE_FIELDS[fieldType]
  99. };
  100. if (isProps['array']) {
  101. ret = {
  102. ...ret,
  103. ...getTypesForArray(fieldType),
  104. decorator: 'ArrayField',
  105. }
  106. }
  107. const names = this.interpolateNames(f, entity);
  108. ret = {
  109. ...ret,
  110. ...this.interpolateNames(f, entity),
  111. relPathForModel: this.relativePathForModel(names['relClassName'])
  112. }
  113. debug(`Mustache Field: ${JSON.stringify(ret, null, 2)}`);
  114. return ret as MustacheField;
  115. }
  116. interpolateNames(f: Field, entity: ObjectType): { [key: string]: string } {
  117. const single = (f.type === 'otm') ? f.name.slice(0, -1) : f.name; // strip s at the end if otm
  118. return {
  119. ...names(single),
  120. relClassName: pascalCase(single),
  121. relCamelName: camelCase(single),
  122. relFieldName: camelCase(entity.name),
  123. relFieldNamePlural: camelPlural(entity.name)
  124. }
  125. }
  126. transform(objType: ObjectType): MustacheObjectType {
  127. const fields: MustacheField[] = [];
  128. objType.fields.map((f) => fields.push(this.transformField(f, objType)));
  129. const has: Props = {};
  130. for (const key in TYPE_FIELDS) {
  131. const _key: string = (key === 'numeric') ? 'numeric' || 'decimal' : key;
  132. has[key] = objType.fields.some((f) => f.columnType() === _key);
  133. }
  134. has['array'] = objType.fields.some((f) => f.isArray());
  135. debug(`ObjectType has: ${JSON.stringify(has, null, 2)}`);
  136. return { fields,
  137. generatedFolderRelPath: this.context["generatedFolderRelPath"] as string, //this.getGeneratedFolderRelativePath(objType.name),
  138. has,
  139. ...names(objType.name) } as MustacheObjectType;
  140. }
  141. generate(mustacheTeplate: string, objType: ObjectType):string {
  142. const mustacheQuery = this.transform(objType);
  143. return Mustache.render(mustacheTeplate, mustacheQuery);
  144. }
  145. relativePathForModel(referenced: string): string {
  146. return path.join(
  147. '..',
  148. kebabCase(referenced),
  149. `${kebabCase(referenced)}.model`
  150. );
  151. }
  152. }