@postxl/generator 0.29.0 → 0.31.0

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.
package/dist/generator.js CHANGED
@@ -61,6 +61,7 @@ const meta_1 = require("./lib/meta");
61
61
  const types_1 = require("./lib/schema/types");
62
62
  const client_path_1 = require("./prisma/client-path");
63
63
  const parse_1 = require("./prisma/parse");
64
+ const datamocker_generator_1 = require("./generators/indices/datamocker.generator");
64
65
  const businesslogicservice_generator_1 = require("./generators/indices/businesslogicservice.generator");
65
66
  const businesslogicindex_generator_1 = require("./generators/indices/businesslogicindex.generator");
66
67
  const businesslogicmodule_generator_1 = require("./generators/indices/businesslogicmodule.generator");
@@ -73,6 +74,7 @@ const CONFIG_SCHEMA = zod_1.z
73
74
  project: zod_1.z.string(),
74
75
  pathToTypes: zod_1.z.string().optional(),
75
76
  pathToDataLib: zod_1.z.string().optional(),
77
+ pathToCypress: zod_1.z.string().optional(),
76
78
  pathToBusinessLogic: zod_1.z.string().optional(),
77
79
  pathToSeedLib: zod_1.z.string().optional(),
78
80
  trpcRoutesFolder: zod_1.z.string().optional(),
@@ -92,6 +94,7 @@ const CONFIG_SCHEMA = zod_1.z
92
94
  project: s.project,
93
95
  paths: {
94
96
  dataLibPath: (0, types_1.toPath)(s.pathToDataLib || 'repos'),
97
+ cypressPath: (0, types_1.toPath)(s.pathToCypress || './e2e/cypress/'),
95
98
  businessLogicPath: (0, types_1.toPath)(s.pathToBusinessLogic || 'repos'),
96
99
  reactFolderPath: (0, types_1.toPath)(s.reactFolderOutput || 'react'),
97
100
  modelTypeDefinitionsPath: (0, types_1.toPath)(s.pathToTypes || 'types'),
@@ -163,6 +166,7 @@ function generate({ models, enums, config, prismaClientPath, logger, }) {
163
166
  generated.write(`/${meta.data.stubFilePath}.ts`, (0, stub_generator_1.generateStub)({ model, meta }));
164
167
  generated.write(`/${meta.data.repoFilePath}.ts`, (0, repository_generator_1.generateRepository)({ model, meta }));
165
168
  generated.write(`/${meta.data.mockRepoFilePath}.ts`, (0, repository_generator_1.generateMockRepository)({ model, meta }));
169
+ generated.write(`/${meta.data.dataMockerStubFilePath}.ts`, (0, stub_generator_1.generateStub)({ model, meta }));
166
170
  }
167
171
  // Business Logic
168
172
  if (!config.disableGenerators.businessLogic) {
@@ -194,6 +198,8 @@ function generate({ models, enums, config, prismaClientPath, logger, }) {
194
198
  generated.write(`/${meta.data.dataMockModuleFilePath}.ts`, (0, datamockmodule_generator_1.generateDataMockModule)({ models, meta }));
195
199
  generated.write(`/${meta.data.dataModuleFilePath}.ts`, (0, datamodule_generator_1.generateDataModule)({ models, meta }));
196
200
  generated.write(`/${meta.data.dataServiceFilePath}.ts`, (0, dataservice_generator_1.generateDataService)({ models, meta }));
201
+ generated.write(`/${meta.data.dataMockerFilePath}.ts`, (0, datamocker_generator_1.generateDataMocker)({ models, meta }));
202
+ generated.write(`/${meta.data.dataMockerStubIndexFilePath}.ts`, (0, stubs_generator_1.generateDataMockerStubsIndex)({ models, meta }));
197
203
  generated.write(`/${meta.data.testDataServiceFilePath}.ts`, (0, testdataservice_generator_1.generateTestDataService)({ models, meta }));
198
204
  generated.write(`/${meta.data.repositoriesConstFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesArray)({ models, meta }));
199
205
  generated.write(`/${meta.data.repositoriesIndexFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesIndex)({ models, meta }));
@@ -0,0 +1,9 @@
1
+ import { SchemaMetaData } from '../../lib/meta';
2
+ import { Model } from '../../lib/schema/schema';
3
+ /**
4
+ * Generates a generic data mocker class.
5
+ */
6
+ export declare function generateDataMocker({ models, meta: schemaMeta }: {
7
+ models: Model[];
8
+ meta: SchemaMetaData;
9
+ }): string;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateDataMocker = void 0;
4
+ const meta_1 = require("../../lib/meta");
5
+ const schema_1 = require("../../lib/schema/schema");
6
+ const imports_1 = require("../../lib/imports");
7
+ /**
8
+ * Generates a generic data mocker class.
9
+ */
10
+ function generateDataMocker({ models, meta: schemaMeta }) {
11
+ const modelMetas = models.map((model) => ({ model, meta: (0, meta_1.getModelMetadata)({ model }) }));
12
+ const imports = imports_1.ImportsGenerator.from(schemaMeta.data.dataMockerFilePath);
13
+ for (const { model, meta } of modelMetas) {
14
+ imports.addImport({
15
+ items: [model.typeName],
16
+ from: meta.types.importPath,
17
+ });
18
+ imports.addImport({
19
+ items: [meta.data.stubGenerationFnName],
20
+ from: schemaMeta.data.dataMockerStubImportPath,
21
+ });
22
+ }
23
+ const publicVariables = modelMetas
24
+ .map(({ model, meta }) => `public ${meta.data.dataServiceName}: ${model.typeName}[] = []`)
25
+ .join('\n');
26
+ const privateIds = modelMetas
27
+ .map(({ model, meta }) => `private ${meta.data.dataServiceIdName}Ids = new IdTracker<${model.typeName}>()`)
28
+ .join('\n');
29
+ const addDataFunctions = modelMetas.map(({ model, meta }) => generateAddDataFunction(model, meta)).join('\n');
30
+ // const timeNow = new Date().toLocaleTimeString('en-US', { hour12: false, hour: 'numeric', minute: 'numeric' })
31
+ // const todayDate = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'numeric', day: 'numeric' })
32
+ return `
33
+ import type { MockData } from '@pxl/data'
34
+ import { IdTracker } from './id-tracker.class'
35
+ ${imports.generate()}
36
+
37
+ export class DataMocker implements MockData {
38
+ ${publicVariables}
39
+
40
+ ${privateIds}
41
+
42
+ ${addDataFunctions}
43
+ }
44
+ `;
45
+ }
46
+ exports.generateDataMocker = generateDataMocker;
47
+ // Generated time stamp: ${todayDate} ${timeNow}
48
+ function generateAddDataFunction(model, meta) {
49
+ const { fields, idField } = model;
50
+ const relations = fields.filter(schema_1.isFieldRelation).map((field) => (Object.assign(Object.assign({}, field), { meta: (0, meta_1.getModelMetadata)({ model: field.relationToModel }), fieldMeta: (0, meta_1.getFieldMetadata)({ field }), model: field.relationToModel })));
51
+ return `public add${model.typeName}(item?: Partial<Omit<${model.typeName}, '${idField.name}'> & { ${idField.name}: ${idField.unbrandedTypeName}}>): DataMocker {
52
+ const initial${meta.data.dataServiceIdName} = stub${model.typeName}(item ?? {})
53
+ this.${meta.data.dataServiceName}.push(this.${meta.data.dataServiceIdName}Ids.ensureId(initial${meta.data.dataServiceIdName}))
54
+ const ${meta.data.dataServiceIdName} = this.${meta.data.dataServiceName}[this.${meta.data.dataServiceName}.length - 1]
55
+ ${relations
56
+ .map((r) => `
57
+ ${r.isRequired
58
+ ? `
59
+ if (${meta.data.dataServiceIdName}.${r.name}) {
60
+ if (this.${r.meta.data.dataServiceName}.findIndex((x) => x.id === ${meta.data.dataServiceIdName}.${r.name}) === -1 ) {
61
+ this.add${r.model.typeName}({ id: ${meta.data.dataServiceIdName}.${r.name} })
62
+ }
63
+ } else {
64
+ if (this.${r.meta.data.dataServiceName}.length === 0) {
65
+ this.add${r.model.typeName}()
66
+ }
67
+ ${meta.data.dataServiceIdName}.${r.name} = this.${r.meta.data.dataServiceName}[0].id
68
+ }
69
+ `
70
+ : `
71
+ if (${meta.data.dataServiceIdName}.${r.name}) {
72
+ if (this.${r.meta.data.dataServiceName}.findIndex((x) => x.id === ${meta.data.dataServiceIdName}.${r.name}) === -1 ) {
73
+ this.add${r.model.typeName}({ id: ${meta.data.dataServiceIdName}.${r.name} })
74
+ }
75
+ }
76
+ `}
77
+ `)
78
+ .join('\n')}
79
+ return this
80
+ }`;
81
+ }
@@ -7,3 +7,10 @@ export declare function generateStubsIndex({ models, meta }: {
7
7
  models: Model[];
8
8
  meta: SchemaMetaData;
9
9
  }): string;
10
+ /**
11
+ * Generates an index file that exports all stubs for the data mocker.
12
+ */
13
+ export declare function generateDataMockerStubsIndex({ models, meta }: {
14
+ models: Model[];
15
+ meta: SchemaMetaData;
16
+ }): string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.generateStubsIndex = void 0;
3
+ exports.generateDataMockerStubsIndex = exports.generateStubsIndex = void 0;
4
4
  const exports_1 = require("../../lib/exports");
5
5
  const meta_1 = require("../../lib/meta");
6
6
  /**
@@ -15,3 +15,15 @@ function generateStubsIndex({ models, meta }) {
15
15
  return exports.generate();
16
16
  }
17
17
  exports.generateStubsIndex = generateStubsIndex;
18
+ /**
19
+ * Generates an index file that exports all stubs for the data mocker.
20
+ */
21
+ function generateDataMockerStubsIndex({ models, meta }) {
22
+ const exports = exports_1.ExportsGenerator.from(meta.data.dataMockerStubIndexFilePath);
23
+ for (const model of models) {
24
+ const meta = (0, meta_1.getModelMetadata)({ model });
25
+ exports.exportEverythingFromPath(meta.data.dataMockerStubFilePath);
26
+ }
27
+ return exports.generate();
28
+ }
29
+ exports.generateDataMockerStubsIndex = generateDataMockerStubsIndex;
@@ -9,12 +9,17 @@ const types_1 = require("../../lib/types");
9
9
  * Generates a stub definition file for a given model.
10
10
  */
11
11
  function generateStub({ model, meta }) {
12
- const { fields } = model;
12
+ const { fields, idField } = model;
13
13
  const imports = imports_1.ImportsGenerator.from(meta.data.stubFilePath).addImport({
14
14
  items: [model.typeName, meta.types.toBrandedIdTypeFnName],
15
15
  from: meta.types.importPath,
16
16
  });
17
- const assignments = getAssignmentStatementModel({ fields, imports });
17
+ const assignments = getAssignmentStatementModel({
18
+ fields,
19
+ nameField: model.nameField,
20
+ modelName: model.name,
21
+ imports,
22
+ });
18
23
  return `
19
24
  ${imports.generate()}
20
25
 
@@ -29,10 +34,11 @@ export const ${meta.data.defaultStubConstantName}: ${model.typeName} = {
29
34
  * Utility function that creates a new ${meta.userFriendlyName} object from a partial object of values.
30
35
  */
31
36
  export const ${meta.data.stubGenerationFnName} = (
32
- item: Partial<${model.typeName}>
37
+ item: Partial<Omit<${model.typeName}, '${idField.name}'> & { ${idField.name}: ${idField.unbrandedTypeName}}>
33
38
  ): ${model.typeName} => ({
34
39
  ...${meta.data.defaultStubConstantName},
35
40
  ...item,
41
+ ${idField.name}: item.${idField.name} ? ${meta.types.toBrandedIdTypeFnName}(item.${idField.name}) : ${meta.data.defaultStubConstantName}.id,
36
42
  })
37
43
  `;
38
44
  }
@@ -40,7 +46,7 @@ exports.generateStub = generateStub;
40
46
  /**
41
47
  * Return an assignment statement for a model where each field is assigned null or its type's default value.
42
48
  */
43
- function getAssignmentStatementModel({ fields, imports }) {
49
+ function getAssignmentStatementModel({ fields, nameField, modelName, imports, }) {
44
50
  return fields
45
51
  .map((f) => {
46
52
  if (!f.isRequired) {
@@ -48,6 +54,9 @@ function getAssignmentStatementModel({ fields, imports }) {
48
54
  }
49
55
  switch (f.kind) {
50
56
  case 'scalar': {
57
+ if ((nameField === null || nameField === void 0 ? void 0 : nameField.name) === f.name) {
58
+ return `${f.name}: '${modelName}'`;
59
+ }
51
60
  return `${f.name}: ${(0, fields_1.getDefaultValueForType)(f.tsTypeName)}`;
52
61
  }
53
62
  case 'id': {
@@ -25,6 +25,18 @@ export type SchemaMetaData = {
25
25
  * Path to the file containing data service class definitions.
26
26
  */
27
27
  dataServiceFilePath: Types.Path;
28
+ /**
29
+ * Path to the file containing data mocker class definitions.
30
+ */
31
+ dataMockerFilePath: Types.Path;
32
+ /**
33
+ * Path to the file containing data mocker class definitions.
34
+ */
35
+ dataMockerStubIndexFilePath: Types.Path;
36
+ /**
37
+ * Path that may be used to import the data mocker stubs.
38
+ */
39
+ dataMockerStubImportPath: Types.Path;
28
40
  /**
29
41
  * Path to the file containing test data service definition.
30
42
  */
@@ -178,6 +190,10 @@ export type ModelMetaData = {
178
190
  * The path to the file containing stub definitions of this model.
179
191
  */
180
192
  stubFilePath: Types.Path;
193
+ /**
194
+ * The path to the file containing stub definitions for the data mocker of this model.
195
+ */
196
+ dataMockerStubFilePath: Types.Path;
181
197
  /**
182
198
  * The name of the TypeScript constant that contains default values for all fields.
183
199
  */
@@ -214,6 +230,10 @@ export type ModelMetaData = {
214
230
  * The name by which the repository is exposed in the dataService/context. (e.g. aggregations)
215
231
  */
216
232
  dataServiceName: Types.VariableName;
233
+ /**
234
+ * The name for internal use of modelIds in data mocker
235
+ */
236
+ dataServiceIdName: Types.VariableName;
217
237
  /**
218
238
  * The name of the export Excel table (e.g. `Aggregations`).
219
239
  */
package/dist/lib/meta.js CHANGED
@@ -38,6 +38,9 @@ function getSchemaMetadata({ config }) {
38
38
  dataMockModuleFilePath: Types.toPath(`${config.paths.dataLibPath}data.mock.module`),
39
39
  testDataServiceFilePath: Types.toPath(`${config.paths.dataLibPath}test-data.service`),
40
40
  dataServiceFilePath: Types.toPath(`${config.paths.dataLibPath}data.service`),
41
+ dataMockerFilePath: Types.toPath(`${config.paths.cypressPath}support/data-mocker.class`),
42
+ dataMockerStubImportPath: Types.toPath(`${config.paths.cypressPath}support/stubs`),
43
+ dataMockerStubIndexFilePath: Types.toPath(`${config.paths.cypressPath}support/stubs/index`),
41
44
  repositoriesConstFilePath: Types.toPath(`${config.paths.dataLibPath}repositories/repositories`),
42
45
  repositoriesIndexFilePath: Types.toPath(`${config.paths.dataLibPath}repositories/index`),
43
46
  stubIndexFilePath: Types.toPath(`${config.paths.dataLibPath}stubs/index`),
@@ -77,7 +80,7 @@ exports.getSchemaMetadata = getSchemaMetadata;
77
80
  */
78
81
  function getModelMetadata({ model }) {
79
82
  const { name, schemaConfig: config } = model;
80
- const { PascalCase, camelCase, pluralized, uncapitalizedPlural, capitalizedPlural } = (0, string_1.conjugateNames)(name);
83
+ const { PascalCase, camelCase, pluralized, uncapitalizedPlural, uncapitalized, capitalizedPlural } = (0, string_1.conjugateNames)(name);
81
84
  return {
82
85
  userFriendlyName: PascalCase,
83
86
  internalSingularName: Types.toVariableName(camelCase),
@@ -90,11 +93,13 @@ function getModelMetadata({ model }) {
90
93
  mockRepoFileName: Types.toFileName(`${camelCase}.mock.repository`),
91
94
  mockRepoFilePath: Types.toPath(`${config.paths.dataLibPath}repositories/mock/${camelCase}.mock.repository`),
92
95
  stubFilePath: Types.toPath(`${config.paths.dataLibPath}stubs/${camelCase}.stub`),
96
+ dataMockerStubFilePath: Types.toPath(`${config.paths.cypressPath}support/stubs/${camelCase}.stub`),
93
97
  importPath: Types.toPath(`@${config.project}/data`),
94
98
  stubGenerationFnName: Types.toFunction(`stub${PascalCase}`),
95
99
  repositoryClassName: Types.toClassName(`${PascalCase}Repository`),
96
100
  mockRepositoryClassName: Types.toClassName(`Mock${PascalCase}Repository`),
97
101
  dataServiceName: Types.toVariableName(`${uncapitalizedPlural}`),
102
+ dataServiceIdName: Types.toVariableName(`${uncapitalized}`),
98
103
  excelExportTableName: `${pluralized}`,
99
104
  repository: {
100
105
  decoderFnName: Types.toFunction(`to${PascalCase}`),
@@ -55,6 +55,10 @@ export type SchemaConfig = {
55
55
  * may reference data library using `@project/data` import.
56
56
  */
57
57
  dataLibPath: Types.Path;
58
+ /**
59
+ * Path to the directory containing Cypress project.
60
+ */
61
+ cypressPath: Types.Path;
58
62
  /**
59
63
  * Path to the directory containing business logic.
60
64
  *
@@ -30,5 +30,6 @@ export declare const conjugateNames: (name: string) => {
30
30
  camelCase: string;
31
31
  pluralized: string;
32
32
  uncapitalizedPlural: string;
33
+ uncapitalized: string;
33
34
  capitalizedPlural: string;
34
35
  };
@@ -111,6 +111,7 @@ const conjugateNames = (name) => ({
111
111
  camelCase: (0, exports.toCamelCase)(name),
112
112
  pluralized: (0, exports.capitalize)((0, exports.pluralize)(name)),
113
113
  uncapitalizedPlural: (0, exports.uncapitalize)((0, exports.pluralize)(name)),
114
+ uncapitalized: (0, exports.uncapitalize)(name),
114
115
  capitalizedPlural: (0, exports.capitalize)((0, exports.pluralize)(name)),
115
116
  });
116
117
  exports.conjugateNames = conjugateNames;
@@ -82,6 +82,9 @@ function parseModel({ dmmfModel, enums, models, config, }) {
82
82
  if (!referencedModel) {
83
83
  (0, error_1.throwError)(`Investigate: Field references unknown model ${dmmfField.type}.`);
84
84
  }
85
+ if (dmmfField.relationOnDelete && dmmfField.relationOnDelete !== 'NoAction') {
86
+ (0, error_1.throwError)(`Investigate: "onDelete" attribute for relationship ${dmmfField.relationName} must be "NoAction": any deletes must be handled in the application layer, e.g. to update repository and search caches!`);
87
+ }
85
88
  relations[dmmfField.relationFromFields[0]] = referencedModel;
86
89
  }
87
90
  const relationFields = dmmfModel.fields
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postxl/generator",
3
- "version": "0.29.0",
3
+ "version": "0.31.0",
4
4
  "main": "./dist/generator.js",
5
5
  "typings": "./dist/generator.d.ts",
6
6
  "bin": {
@@ -22,7 +22,7 @@
22
22
  "prettier": "^2.8.8",
23
23
  "remeda": "1.9.4",
24
24
  "zod": "3.21.4",
25
- "@postxl/lock": "0.4.8"
25
+ "@postxl/lock": "0.4.9"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@prisma/client": "^5.2.0",
@@ -46,7 +46,7 @@
46
46
  "build": "tsc -p tsconfig.build.json",
47
47
  "prepublish": "tsc -p tsconfig.build.json",
48
48
  "dev": "tsc -p tsconfig.build.json -w",
49
- "test:generation": "./scripts/test.sh",
49
+ "test:generators": "./scripts/test.sh",
50
50
  "test:jest": "jest",
51
51
  "test:watch": "jest --watch",
52
52
  "test:types": "tsc --noEmit"