@postxl/generator 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/README.md +3 -0
  2. package/dist/jest.config.d.ts +3 -0
  3. package/dist/src/generator.d.ts +12 -0
  4. package/dist/src/generator.js +164 -0
  5. package/dist/src/generators/enums/react.generator.d.ts +10 -0
  6. package/dist/src/generators/enums/react.generator.js +81 -0
  7. package/dist/src/generators/enums/types.generator.d.ts +10 -0
  8. package/dist/src/generators/enums/types.generator.js +18 -0
  9. package/dist/src/generators/indices/datamockmodule.generator.d.ts +9 -0
  10. package/dist/src/generators/indices/datamockmodule.generator.js +104 -0
  11. package/dist/src/generators/indices/datamodule.generator.d.ts +9 -0
  12. package/dist/src/generators/indices/datamodule.generator.js +128 -0
  13. package/dist/src/generators/indices/dataservice.generator.d.ts +9 -0
  14. package/dist/src/generators/indices/dataservice.generator.js +47 -0
  15. package/dist/src/generators/indices/repositories.generator.d.ts +9 -0
  16. package/dist/src/generators/indices/repositories.generator.js +17 -0
  17. package/dist/src/generators/indices/seed.generator.d.ts +9 -0
  18. package/dist/src/generators/indices/seed.generator.js +17 -0
  19. package/dist/src/generators/indices/stubs.generator.d.ts +9 -0
  20. package/dist/src/generators/indices/stubs.generator.js +17 -0
  21. package/dist/src/generators/indices/testdataservice.generator.d.ts +7 -0
  22. package/dist/src/generators/indices/testdataservice.generator.js +61 -0
  23. package/dist/src/generators/indices/types.generator.d.ts +10 -0
  24. package/dist/src/generators/indices/types.generator.js +21 -0
  25. package/dist/src/generators/models/react.generator/context.generator.d.ts +9 -0
  26. package/dist/src/generators/models/react.generator/context.generator.js +66 -0
  27. package/dist/src/generators/models/react.generator/index.d.ts +10 -0
  28. package/dist/src/generators/models/react.generator/index.js +32 -0
  29. package/dist/src/generators/models/react.generator/library.generator.d.ts +9 -0
  30. package/dist/src/generators/models/react.generator/library.generator.js +113 -0
  31. package/dist/src/generators/models/react.generator/lookup.generator.d.ts +9 -0
  32. package/dist/src/generators/models/react.generator/lookup.generator.js +97 -0
  33. package/dist/src/generators/models/react.generator/modals.generator.d.ts +23 -0
  34. package/dist/src/generators/models/react.generator/modals.generator.js +521 -0
  35. package/dist/src/generators/models/repository.generator.d.ts +9 -0
  36. package/dist/src/generators/models/repository.generator.js +282 -0
  37. package/dist/src/generators/models/route.generator.d.ts +16 -0
  38. package/dist/src/generators/models/route.generator.js +112 -0
  39. package/dist/src/generators/models/seed.generator.d.ts +20 -0
  40. package/dist/src/generators/models/seed.generator.js +185 -0
  41. package/dist/src/generators/models/stub.generator.d.ts +9 -0
  42. package/dist/src/generators/models/stub.generator.js +74 -0
  43. package/dist/src/generators/models/types.generator.d.ts +9 -0
  44. package/dist/src/generators/models/types.generator.js +116 -0
  45. package/dist/src/lib/attributes.d.ts +43 -0
  46. package/dist/src/lib/attributes.js +2 -0
  47. package/dist/src/lib/exports.d.ts +26 -0
  48. package/dist/src/lib/exports.js +38 -0
  49. package/dist/src/lib/imports.d.ts +35 -0
  50. package/dist/src/lib/imports.js +55 -0
  51. package/dist/src/lib/meta.d.ts +359 -0
  52. package/dist/src/lib/meta.js +195 -0
  53. package/dist/src/lib/schema/fields.d.ts +35 -0
  54. package/dist/src/lib/schema/fields.js +49 -0
  55. package/dist/src/lib/schema/schema.d.ts +275 -0
  56. package/dist/src/lib/schema/schema.js +2 -0
  57. package/dist/src/lib/schema/types.d.ts +72 -0
  58. package/dist/src/lib/schema/types.js +41 -0
  59. package/dist/src/lib/schema/zod.d.ts +8 -0
  60. package/dist/src/lib/schema/zod.js +44 -0
  61. package/dist/src/lib/serializer.d.ts +15 -0
  62. package/dist/src/lib/serializer.js +24 -0
  63. package/dist/src/lib/utils/error.d.ts +5 -0
  64. package/dist/src/lib/utils/error.js +13 -0
  65. package/dist/src/lib/utils/file.d.ts +10 -0
  66. package/dist/src/lib/utils/file.js +54 -0
  67. package/dist/src/lib/utils/logger.d.ts +11 -0
  68. package/dist/src/lib/utils/logger.js +2 -0
  69. package/dist/src/lib/utils/string.d.ts +29 -0
  70. package/dist/src/lib/utils/string.js +75 -0
  71. package/dist/src/lib/utils/types.d.ts +12 -0
  72. package/dist/src/lib/utils/types.js +2 -0
  73. package/dist/src/lib/vfs.d.ts +137 -0
  74. package/dist/src/lib/vfs.js +419 -0
  75. package/dist/src/prisma/attributes.d.ts +17 -0
  76. package/dist/src/prisma/attributes.js +80 -0
  77. package/dist/src/prisma/client-path.d.ts +7 -0
  78. package/dist/src/prisma/client-path.js +29 -0
  79. package/dist/src/prisma/parse.d.ts +12 -0
  80. package/dist/src/prisma/parse.js +276 -0
  81. package/dist/tests/attributes.test.d.ts +1 -0
  82. package/dist/tests/attributes.test.js +76 -0
  83. package/dist/tests/file.test.d.ts +1 -0
  84. package/dist/tests/file.test.js +26 -0
  85. package/dist/tests/utils/random.d.ts +3 -0
  86. package/dist/tests/utils/random.js +15 -0
  87. package/dist/tests/vfs.test.d.ts +1 -0
  88. package/dist/tests/vfs.test.js +74 -0
  89. package/dist/tsconfig.tsbuildinfo +1 -0
  90. package/jest.config.ts +18 -0
  91. package/package.json +42 -0
  92. package/tests/attributes.test.ts +91 -0
  93. package/tests/file.test.ts +32 -0
  94. package/tests/utils/random.ts +11 -0
  95. package/tests/vfs.test.ts +92 -0
@@ -0,0 +1,282 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateRepository = void 0;
4
+ const fields_1 = require("../../lib/schema/fields");
5
+ const string_1 = require("../../lib/utils/string");
6
+ const imports_1 = require("../../lib/imports");
7
+ /**
8
+ * Generates repository data structure for a given model.
9
+ */
10
+ function generateRepository({ model, meta }) {
11
+ var _a;
12
+ const { idField, fields } = model;
13
+ const decoder = meta.data.repository.decoderFnName;
14
+ const uniqueStringFields = fields.filter(fields_1.isUniqueStringField);
15
+ const defaultValueInitFn = `
16
+ if (item.${(_a = model.defaultField) === null || _a === void 0 ? void 0 : _a.name}) {
17
+ if (this.defaultValue) {
18
+ console.warn(\`More than one default ${meta.userFriendlyName} found! \${this.defaultValue.id} and \${item.id}\`)
19
+ }
20
+ this.defaultValue = item
21
+ }
22
+ `;
23
+ const defaultValueInitCheckFn = `
24
+ if (!this.db.isCLI && !this.defaultValue) {
25
+ throw new Error('No default ${meta.userFriendlyName} found!')
26
+ }
27
+ `;
28
+ const { isGenerated } = idField;
29
+ const idIntInitFn = `this.currentMaxId = (await this.db.${meta.data.repository.getMethodFnName}.aggregate({ _max: { ${idField.sourceName}: true } }))._max.${idField.sourceName} ?? 0`;
30
+ const imports = imports_1.ImportsGenerator.from(meta.data.repoFilePath)
31
+ .addImport({
32
+ items: [model.typeName, model.brandedIdType, meta.types.toBrandedIdTypeFnName],
33
+ from: meta.types.importPath,
34
+ })
35
+ .addImport({ items: [meta.types.zodDecoderFnName], from: meta.types.importPath });
36
+ return `
37
+ import { Injectable, Logger } from '@nestjs/common'
38
+ import { DbService } from '@${model.schemaConfig.project}/db'
39
+
40
+ import { Repository } from '../repository.type'
41
+
42
+ ${imports.generate()}
43
+
44
+ @Injectable()
45
+ export class ${meta.data.repositoryClassName} implements Repository<
46
+ ${model.typeName},
47
+ ${idField.unbrandedTypeName},
48
+ Omit<${model.typeName}, 'id'>
49
+ > {
50
+ protected data: Map<${model.brandedIdType}, ${model.typeName}> = new Map()
51
+ protected logger = new Logger(${meta.data.repositoryClassName}.name)
52
+
53
+ ${model.defaultField ? `public defaultValue!: ${model.typeName}` : ''}
54
+
55
+ ${isGenerated ? `protected currentMaxId = 0\n` : ''}
56
+
57
+ protected uniqueIds = {
58
+ ${uniqueStringFields.map((f) => `'${f.name}': new Map<string, ${model.typeName}>()`).join(',\n')}
59
+ }
60
+
61
+ constructor(protected db: DbService) {}
62
+
63
+ public async init() {
64
+ this.data.clear()
65
+
66
+ this.logger.log(\`Loading ${model.typeName} instances into repository...\`)
67
+
68
+ const data = await this.db.${meta.data.repository.getMethodFnName}.findMany({})
69
+
70
+ for (const rawItem of data) {
71
+ const item = this.${decoder}(rawItem)
72
+ this.data.set(item.id, item)
73
+
74
+ ${model.defaultField ? defaultValueInitFn : ''}
75
+ ${uniqueStringFields.map((f) => `this.uniqueIds.${f}.set(item.${f}, item)`).join('\n')}
76
+ }
77
+
78
+ ${isGenerated ? idIntInitFn : ''}
79
+
80
+ ${model.defaultField ? defaultValueInitCheckFn : ''}
81
+
82
+ this.logger.log(\`Loaded \${this.data.size} instances of ${model.typeName} into repository!\`)
83
+ }
84
+
85
+ public async reInit(data: ${model.typeName}[]): Promise<void> {
86
+ if (!this.db.useE2ETestDB) {
87
+ const errorMsg =
88
+ 'ReInit() shall only be called in tests using MockRepositories or in DB configured for E2E tests!'
89
+ this.logger.error(errorMsg)
90
+ throw new Error(errorMsg)
91
+ }
92
+ await this.db.runOnlyOnTestDb(() => this.db.${meta.data.repository.getMethodFnName}.createMany({ data: data.map((i) => this.toCreateItem(i)) }))
93
+ return this.init()
94
+ }
95
+
96
+ public get(id: ${model.brandedIdType}| ${idField.unbrandedTypeName} | null): ${model.typeName} | null {
97
+ if (id === null) return null
98
+ return this.data.get(${meta.types.toBrandedIdTypeFnName}(id)) ?? null
99
+ }
100
+
101
+ public getAll(): Map<${model.brandedIdType}, ${model.typeName}> {
102
+ return this.data
103
+ }
104
+
105
+ public getAllAsArray(): ${model.typeName}[] {
106
+ return Array.from(this.data.values())
107
+ }
108
+
109
+ public filter(predicate: (item: ${model.typeName}) => boolean): ${model.typeName}[] {
110
+ return this.getAllAsArray().filter(predicate)
111
+ }
112
+
113
+ public findFirst(predicate: (item: ${model.typeName}) => boolean): ${model.typeName} | null {
114
+ return this.getAllAsArray().find(predicate) ?? null
115
+ }
116
+
117
+ public count(): number {
118
+ return this.data.size
119
+ }
120
+
121
+ private toCreateItem(item: ${model.typeName}) {
122
+ ${isGenerated
123
+ ? `if (item.${idField.name} > ++this.currentMaxId) {
124
+ this.currentMaxId = item.${idField.name}
125
+ } else {
126
+ item.${idField.name} = this.currentMaxId as ${model.brandedIdType}
127
+ }`
128
+ : ''}
129
+
130
+ ${uniqueStringFields.map((f) => `this.${getEnsureUniqueFnName(f)}(item)`).join('\n')}
131
+
132
+ return {
133
+ ${[...model.fields.values()].map((f) => `${f.sourceName}: item.${f.name}`).join(',\n')}
134
+ }
135
+ }
136
+ public async createWithId(item: Omit<${model.typeName}, '${idField.name}'> & {
137
+ id: ${model.brandedIdType} | ${idField.unbrandedTypeName} | undefined
138
+ }): Promise<${model.typeName}> {
139
+ if (item.${idField.name} === undefined) {
140
+ return this.create(item)
141
+ }
142
+
143
+ const newItem = this.${decoder}(
144
+ await this.db.${meta.data.repository.getMethodFnName}.create({
145
+ data: this.toCreateItem(item as ${model.typeName}),
146
+ }),
147
+ )
148
+
149
+ ${uniqueStringFields.map((f) => `this.uniqueIds.${f.name}.set(newItem.${f.name}, newItem)`).join('\n')}
150
+
151
+ this.data.set(newItem.id, newItem)
152
+ return newItem
153
+ }
154
+
155
+ public async create(
156
+ item: Omit<${model.typeName}, 'id'>
157
+ ): Promise<${model.typeName}> {
158
+ ${isGenerated ? `const id = ++this.currentMaxId\n` : ''}
159
+
160
+ ${uniqueStringFields.map((f) => `this.${getEnsureUniqueFnName(f)}(item)`).join('\n')}
161
+
162
+ const newItem = this.${decoder}(
163
+ await this.db.${meta.data.repository.getMethodFnName}.create({
164
+ data: {
165
+ ${isGenerated ? `${idField.sourceName}: id,` : ''}
166
+ ${[...model.fields.values()]
167
+ .filter((f) => f.kind !== 'id')
168
+ .map((f) => `${f.sourceName}: item.${f.name}`)
169
+ .join(',\n')}
170
+ },
171
+ }),
172
+ )
173
+
174
+ ${uniqueStringFields.map((f) => `this.uniqueIds.${f.name}.set(newItem.${f.name}, newItem)`).join('\n')}
175
+
176
+ this.data.set(newItem.id, newItem)
177
+ return newItem
178
+ }
179
+
180
+ public async update(item: Partial<${model.typeName}> & {
181
+ id: ${model.brandedIdType} | ${idField.unbrandedTypeName}
182
+ }): Promise<${model.typeName}> {
183
+ const id = ${meta.types.toBrandedIdTypeFnName}(item.id)
184
+ const existingItem = this.get(id)
185
+
186
+ if (!existingItem) {
187
+ throw new Error(\`Could not update ${meta.userFriendlyName} with id \${id}. Not found!\`)
188
+ }
189
+
190
+ ${uniqueStringFields
191
+ .map((f) => {
192
+ return `
193
+ if (item.${f.name} !== undefined && existingItem.${f} !== item.${f}) {
194
+ this.${getEnsureUniqueFnName(f)}(item)
195
+ }
196
+ `;
197
+ })
198
+ .join('\n')}
199
+
200
+ const newItem = this.${decoder}(
201
+ await this.db.${meta.data.repository.getMethodFnName}.update({
202
+ where: {
203
+ ${idField.sourceName}: item.${idField.name},
204
+ },
205
+ data: {
206
+ ${[...model.fields.values()]
207
+ .filter((f) => f.kind !== 'id')
208
+ .map((f) => f.isRequired
209
+ ? `${f.sourceName}: item.${f.name} ?? existingItem.${f.name}`
210
+ : `${f.sourceName}: item.${f.name}`)
211
+ .join(',\n')}
212
+
213
+
214
+ },
215
+ }),
216
+ )
217
+
218
+ ${uniqueStringFields
219
+ .map((f) => {
220
+ return `
221
+ if (item.${f.name} !== undefined && existingItem.${f.name} !== item.${f.name}) {
222
+ this.uniqueIds.${f.name}.delete(existingItem.${f.name})
223
+ if (item.${f.name} !== null) {
224
+ this.uniqueIds.${f.name}.set(newItem.${f.name}, newItem)
225
+ }
226
+ }
227
+ `;
228
+ })
229
+ .join('\n')}
230
+
231
+ this.data.set(newItem.id, newItem)
232
+ return newItem
233
+ }
234
+
235
+ public async delete(id: ${model.brandedIdType} | ${idField.unbrandedTypeName}): Promise<void> {
236
+ const existingItem = this.get(id)
237
+ if (!existingItem) {
238
+ throw new Error(\`Could not delete ${model.typeName} with id \${id}. Not found!\`)
239
+ }
240
+
241
+ await this.db.${meta.data.repository.getMethodFnName}.delete({ where: { ${idField.sourceName}:id } })
242
+
243
+ ${uniqueStringFields.map((f) => `this.uniqueIds.${f.name}.delete(existingItem.${f.name})`).join('\n')}
244
+
245
+ this.data.delete(${meta.types.toBrandedIdTypeFnName}(id))
246
+ }
247
+
248
+ ${uniqueStringFields
249
+ .map((f) => {
250
+ const fName = f.name;
251
+ return `
252
+ private ${getEnsureUniqueFnName(f)}(item: { ${fName}?: string }) {
253
+ if (!item.${fName}) return
254
+ if (!this.uniqueIds.${fName}.has(item.${fName})) return
255
+ let counter = 1
256
+ let ${fName}: string
257
+
258
+ //TODO: Add attribute for max length of string
259
+ do {
260
+ ${fName} = \`\${item.${fName}} (\${++counter})\`
261
+ } while (this.uniqueIds.${fName}.has(${fName}))
262
+
263
+ this.logger.log(\`Asset ${fName} \${item.${fName}} already exists. Renaming to \${${fName}})\`)
264
+ item.${fName} = ${fName}
265
+ }
266
+ `;
267
+ })
268
+ .join('\n\n')}
269
+
270
+ /**
271
+ * Utility function that converts a given Database object to a TypeScript model instance
272
+ */
273
+ private ${decoder}(item: unknown): ${model.typeName} {
274
+ return ${meta.types.zodDecoderFnName}.parse(item)
275
+ }
276
+ }
277
+ `;
278
+ }
279
+ exports.generateRepository = generateRepository;
280
+ function getEnsureUniqueFnName(field) {
281
+ return `ensureUnique${(0, string_1.toPascalCase)(field.name)}`;
282
+ }
@@ -0,0 +1,16 @@
1
+ import { ModelMetaData, SchemaMetaData } from '../../lib/meta';
2
+ import { Model } from '../../lib/schema/schema';
3
+ /**
4
+ * Generates TRPC route for a given model.
5
+ */
6
+ export declare function generateRoute({ model, meta }: {
7
+ model: Model;
8
+ meta: ModelMetaData;
9
+ }): string;
10
+ /**
11
+ * Generates the index file for all the routes.
12
+ */
13
+ export declare function generateRoutesIndex({ models, meta }: {
14
+ models: Model[];
15
+ meta: SchemaMetaData;
16
+ }): string;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateRoutesIndex = exports.generateRoute = void 0;
4
+ const meta_1 = require("../../lib/meta");
5
+ const fields_1 = require("../../lib/schema/fields");
6
+ const zod_1 = require("../../lib/schema/zod");
7
+ const imports_1 = require("../../lib/imports");
8
+ /**
9
+ * Generates TRPC route for a given model.
10
+ */
11
+ function generateRoute({ model, meta }) {
12
+ const { idField, defaultField } = model;
13
+ const defaultValueMethod = `
14
+ getDefault: procedure.query(({ ctx }) => ctx.dataService.${meta.data.dataServiceName}.defaultValue),
15
+ `;
16
+ const createMethod = getCreateMethod({ model, meta });
17
+ const updateMethod = getUpdateMethod({ model, meta });
18
+ const deleteMethod = getDeleteMethod({ idField, meta });
19
+ const imports = imports_1.ImportsGenerator.from(meta.trpc.routerFilePath).addImport({
20
+ items: [meta.types.toBrandedIdTypeFnName],
21
+ from: meta.types.importPath,
22
+ });
23
+ for (const relation of (0, fields_1.getRelationFields)(model)) {
24
+ const depMeta = (0, meta_1.getModelMetadata)({ model: relation.relationToModel });
25
+ imports.addImport({
26
+ items: [depMeta.types.toBrandedIdTypeFnName],
27
+ from: depMeta.types.importPath,
28
+ });
29
+ }
30
+ return `
31
+ import { z } from 'zod'
32
+ import { procedure, router, undefinedToNull } from '../helper'
33
+
34
+ ${imports.generate()}
35
+
36
+ export const ${meta.trpc.routerName} = router({
37
+ ${defaultField ? defaultValueMethod : ''}
38
+
39
+ get: procedure
40
+ .input(z.${idField.unbrandedTypeName}().transform(${meta.types.toBrandedIdTypeFnName}))
41
+ .query(async ({ input, ctx }) => await ctx.dataService.${meta.data.dataServiceName}.get(input)),
42
+ getMap: procedure.query(({ ctx }) => ctx.dataService.${meta.data.dataServiceName}.getAll()),
43
+
44
+ ${omit(createMethod, model.attributes.skipCreate)}
45
+ ${omit(updateMethod, model.attributes.skipUpdate)}
46
+ ${omit(deleteMethod, model.attributes.skipDelete)}
47
+ })
48
+ `;
49
+ }
50
+ exports.generateRoute = generateRoute;
51
+ function omit(lines, omitted) {
52
+ if (!omitted) {
53
+ return lines;
54
+ }
55
+ return lines
56
+ .split('\n')
57
+ .map((l) => `// ${l}`)
58
+ .join('\n');
59
+ }
60
+ function getCreateMethod({ model: { fields }, meta }) {
61
+ const parameters = fields
62
+ .filter((f) => f.kind !== 'id')
63
+ .map((field) => `${field.name}: z.${(0, zod_1.getZodDecoderDefinition)({ field })}`)
64
+ .join(',');
65
+ return `
66
+ create: procedure.input(z.object({
67
+ ${parameters}
68
+ }))
69
+ .mutation(({ input, ctx }) => ctx.dataService.${meta.data.dataServiceName}.create(input)),
70
+ `;
71
+ }
72
+ function getUpdateMethod({ model: { fields }, meta }) {
73
+ const parameters = fields
74
+ .map((field) => `${field.name}: z.${(0, zod_1.getZodDecoderDefinition)({ field, allowAnyOptionalField: field.kind !== 'id' })}`)
75
+ .join(',');
76
+ return `update: procedure
77
+ .input(
78
+ z.object({
79
+ ${parameters}
80
+ })
81
+ )
82
+ .mutation(({ input, ctx }) => ctx.dataService.${meta.data.dataServiceName}.update(input)),
83
+ `;
84
+ }
85
+ function getDeleteMethod({ idField, meta }) {
86
+ return `
87
+ delete: procedure
88
+ .input(z.${idField.unbrandedTypeName}().transform(${meta.types.toBrandedIdTypeFnName}))
89
+ .mutation(({ input, ctx }) => ctx.dataService.${meta.data.dataServiceName}.delete(input)),
90
+ `;
91
+ }
92
+ /**
93
+ * Generates the index file for all the routes.
94
+ */
95
+ function generateRoutesIndex({ models, meta }) {
96
+ const mm = models.map((model) => ({ model, meta: (0, meta_1.getModelMetadata)({ model }) }));
97
+ const imports = imports_1.ImportsGenerator.from(meta.trpc.routesFilePath);
98
+ for (const { meta } of mm) {
99
+ imports.addImport({ items: [meta.trpc.routerName], from: meta.trpc.routerFilePath });
100
+ }
101
+ return `
102
+ ${imports.generate()}
103
+
104
+ /**
105
+ * Object with all generated routes.
106
+ */
107
+ export const routes = {
108
+ ${mm.map(({ meta }) => `${meta.trpc.routerName}`).join(',\n')}
109
+ }
110
+ `;
111
+ }
112
+ exports.generateRoutesIndex = generateRoutesIndex;
@@ -0,0 +1,20 @@
1
+ import { Model, SchemaConfig } from '../../lib/schema/schema';
2
+ import { ModelMetaData } from '../../lib/meta';
3
+ /**
4
+ * Creates a seed file for a given model.
5
+ */
6
+ export declare function generateSeedModel({ model, itemCount, meta, }: {
7
+ model: Model;
8
+ itemCount: number;
9
+ meta: ModelMetaData;
10
+ }): string;
11
+ /**
12
+ * Creates the seed file that exposes all seed data as single object.
13
+ */
14
+ export declare function generateSeeds({ models, config, }: {
15
+ models: {
16
+ model: Model;
17
+ meta: ModelMetaData;
18
+ }[];
19
+ config: SchemaConfig;
20
+ }): string;
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generateSeeds = exports.generateSeedModel = void 0;
7
+ const faker_1 = require("@faker-js/faker");
8
+ const assert_never_1 = __importDefault(require("assert-never"));
9
+ const serializer_1 = require("../../lib/serializer");
10
+ const string_1 = require("../../lib/utils/string");
11
+ const meta_1 = require("../../lib/meta");
12
+ const fields_1 = require("../../lib/schema/fields");
13
+ const imports_1 = require("../../lib/imports");
14
+ /**
15
+ * Creates a seed file for a given model.
16
+ */
17
+ function generateSeedModel({ model, itemCount, meta, }) {
18
+ const imports = imports_1.ImportsGenerator.from(meta.seed.filePath).addImport({
19
+ items: [model.typeName, meta.types.toBrandedIdTypeFnName],
20
+ from: meta.types.importPath,
21
+ });
22
+ for (const relation of (0, fields_1.getRelationFields)(model)) {
23
+ const depMeta = (0, meta_1.getModelMetadata)({ model: relation.relationToModel });
24
+ imports.addImport({
25
+ items: [depMeta.types.toBrandedIdTypeFnName],
26
+ from: depMeta.types.importPath,
27
+ });
28
+ }
29
+ const exampleMode = getExampleMode({ model, maxItemCount: itemCount });
30
+ const seed = [];
31
+ for (let i = 1; i <= exampleMode.itemCount; i++) {
32
+ seed.push(generateSeedData({ model, index: i, exampleMode }));
33
+ }
34
+ return `
35
+ ${imports.generate()}
36
+
37
+ export const ${meta.seed.constantName}: ${model.typeName}[] = [
38
+ ${seed.join(',\n')}
39
+ ]
40
+ `;
41
+ }
42
+ exports.generateSeedModel = generateSeedModel;
43
+ /**
44
+ * In case of a model with examples, there are two options:
45
+ * 1. the number of examples per field is the same for all fields with examples -> we generate them as tuples
46
+ * 2. the number of examples per field is different -> we generate them as random permutations
47
+ * Here we determine which option to use - and the number of tuples/permutations available
48
+ */
49
+ function getExampleMode({ model, maxItemCount }) {
50
+ let numberOfExamplesPerField = undefined;
51
+ let sameNumberOfExamplesPerField = true;
52
+ let examplePermutations = 0;
53
+ for (const field of model.fields) {
54
+ if (field.kind === 'scalar' && field.attributes.examples && field.attributes.examples.length > 0) {
55
+ if (numberOfExamplesPerField === undefined) {
56
+ numberOfExamplesPerField = field.attributes.examples.length;
57
+ examplePermutations = 1;
58
+ }
59
+ else if (numberOfExamplesPerField !== field.attributes.examples.length) {
60
+ sameNumberOfExamplesPerField = false;
61
+ }
62
+ examplePermutations *= field.attributes.examples.length;
63
+ }
64
+ }
65
+ if (numberOfExamplesPerField === undefined) {
66
+ return { mode: 'NoExamples', itemCount: maxItemCount };
67
+ }
68
+ else if (sameNumberOfExamplesPerField) {
69
+ return {
70
+ mode: 'Tuples',
71
+ itemCount: numberOfExamplesPerField < maxItemCount ? numberOfExamplesPerField : maxItemCount,
72
+ };
73
+ }
74
+ else {
75
+ return { mode: 'Permutations', itemCount: examplePermutations < maxItemCount ? examplePermutations : maxItemCount };
76
+ }
77
+ }
78
+ function generateSeedData({ model, index, exampleMode, }) {
79
+ const data = new serializer_1.Serializer();
80
+ data.append('{');
81
+ for (const field of model.fields.values()) {
82
+ data.append(`${field.name}: ${generateFieldData({ field, model, index, exampleMode })},`);
83
+ }
84
+ data.append('}');
85
+ return data.print();
86
+ }
87
+ function generateFieldData({ field, model, index, exampleMode, }) {
88
+ switch (field.kind) {
89
+ case 'id':
90
+ return generateFieldDataId({ field, model, index });
91
+ case 'scalar':
92
+ return generateFieldDataScalar({ field, model, index, exampleMode });
93
+ case 'relation':
94
+ return generateFieldDataRelation({ field, model, index, itemCount: exampleMode.itemCount });
95
+ case 'enum':
96
+ return generateFieldDataEnum({ field });
97
+ default:
98
+ (0, assert_never_1.default)(field);
99
+ }
100
+ }
101
+ function generateFieldDataId({ field, model, index }) {
102
+ const idModelMeta = (0, meta_1.getModelMetadata)({ model: field.model });
103
+ return `${idModelMeta.types.toBrandedIdTypeFnName}(${field.unbrandedTypeName === 'string' ? `'${index}'` : index})`;
104
+ }
105
+ function generateFieldDataScalar({ field, model, index, exampleMode, }) {
106
+ const { hasExample, example } = getFieldExample({ field, model, index, exampleMode });
107
+ switch (field.typeName) {
108
+ case 'string':
109
+ return `'${hasExample ? example : generateFieldDataString({ field, model, index })}'`;
110
+ case 'number':
111
+ return hasExample ? example : generateFieldDataNumber({ field, model, index });
112
+ case 'boolean':
113
+ return hasExample ? example : generateFieldDataBoolean({ field, model, index });
114
+ case 'Date':
115
+ return hasExample ? example : generateFieldDataDate({ field, model, index });
116
+ default:
117
+ console.warn(`Unknown scalar type: ${field.typeName}`);
118
+ return '';
119
+ }
120
+ }
121
+ function getFieldExample({ field, model, index, exampleMode, }) {
122
+ if (exampleMode.mode === 'NoExamples')
123
+ return { hasExample: false, example: undefined };
124
+ if (!field.attributes.examples || field.attributes.examples.length === 0)
125
+ return { hasExample: false, example: undefined };
126
+ if (exampleMode.mode === 'Permutations') {
127
+ const example = faker_1.faker.helpers.arrayElement(field.attributes.examples);
128
+ return { hasExample: true, example };
129
+ }
130
+ else if (exampleMode.mode === 'Tuples') {
131
+ const example = field.attributes.examples[index % field.attributes.examples.length];
132
+ return { hasExample: true, example };
133
+ }
134
+ else {
135
+ (0, assert_never_1.default)(exampleMode);
136
+ }
137
+ }
138
+ function generateFieldDataString({ field, model, index }) {
139
+ if (field.name === 'name')
140
+ return `${(0, string_1.toPascalCase)(model.name)} ${index}`;
141
+ if (field.name === 'email')
142
+ return faker_1.faker.internet.email();
143
+ return faker_1.faker.lorem.words(3);
144
+ }
145
+ function generateFieldDataNumber({}) {
146
+ return faker_1.faker.datatype.float({ precision: 0.1, min: 0, max: 1 }).toString();
147
+ }
148
+ function generateFieldDataBoolean({}) {
149
+ return faker_1.faker.datatype.boolean().toString();
150
+ }
151
+ function generateFieldDataDate({ field, model, index }) {
152
+ return `new Date('${faker_1.faker.date.past(3)}')`;
153
+ }
154
+ function generateFieldDataRelation({ field, model, index, itemCount, }) {
155
+ const referenceId = faker_1.faker.datatype.number({ min: 1, max: itemCount });
156
+ const refModelMeta = (0, meta_1.getModelMetadata)({ model: field.relationToModel });
157
+ const brandingFn = refModelMeta.types.toBrandedIdTypeFnName;
158
+ return `${brandingFn}(${field.unbrandedTypeName === 'string' ? `'${referenceId}'` : referenceId})`;
159
+ }
160
+ function generateFieldDataEnum({ field }) {
161
+ return `'${faker_1.faker.helpers.arrayElement(field.enumerator.values)}'`;
162
+ }
163
+ /**
164
+ * Creates the seed file that exposes all seed data as single object.
165
+ */
166
+ function generateSeeds({ models, config, }) {
167
+ const imports = imports_1.ImportsGenerator.from(config.paths.seedPath);
168
+ for (const { meta } of models) {
169
+ imports.addImport({
170
+ items: [meta.seed.constantName],
171
+ from: meta.seed.filePath,
172
+ });
173
+ }
174
+ const seeds = models.map(({ meta }) => `${meta.seed.constantName}`).join(',\n');
175
+ return `
176
+ import { MockData } from '${config.paths.seedPath}'
177
+
178
+ ${imports.generate()}
179
+
180
+ export const seed: MockData = {
181
+ ${seeds}
182
+ }
183
+ `;
184
+ }
185
+ exports.generateSeeds = generateSeeds;
@@ -0,0 +1,9 @@
1
+ import { Model } from '../../lib/schema/schema';
2
+ import { ModelMetaData } from '../../lib/meta';
3
+ /**
4
+ * Generates a stub definition file for a given model.
5
+ */
6
+ export declare function generateStub({ model, meta }: {
7
+ model: Model;
8
+ meta: ModelMetaData;
9
+ }): string;