@postxl/generator 0.45.0 → 0.47.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/README.md CHANGED
@@ -1,3 +1,22 @@
1
1
  # PXL Generator
2
2
 
3
- A utility package that lets you move quickly and generate basic components of your app easily.
3
+ A utility package that lets you move quickly and generate basic components of your app easily.
4
+
5
+ #### Linking Generator to Template
6
+
7
+ To use the latest version of the generator link the local dependency using [`pnpm link`](https://pnpm.io/cli/link) command.
8
+
9
+ ```sh
10
+ # /
11
+ pnpm build
12
+
13
+ # /packages/manager/template/
14
+ pnpm link ../../generator/
15
+
16
+ # /
17
+ pnpm prisma generate
18
+ ```
19
+
20
+ > If you've correctly linked the generator, you should see a message when running `pnpm i`.
21
+
22
+ ![pnpm link](./media/link.png)
package/dist/generator.js CHANGED
@@ -93,7 +93,7 @@ const CONFIG_SCHEMA = zod_1.z
93
93
  pathToTypes: zod_1.z.string().optional(),
94
94
  trpcRoutesFolder: zod_1.z.string().optional(),
95
95
  reactFolderOutput: zod_1.z.string().optional(),
96
- migrationsFolder: zod_1.z.string().optional(),
96
+ prismaMigrationsFolder: zod_1.z.string().optional(),
97
97
  randomSeed: zod_1.z
98
98
  .string()
99
99
  .optional()
@@ -106,31 +106,21 @@ const CONFIG_SCHEMA = zod_1.z
106
106
  .transform((s) => {
107
107
  return {
108
108
  paths: {
109
- dataLibPath: (0, types_1.toPath)(s.pathToDataLib || 'repos'),
109
+ dataLibPath: (0, types_1.toPath)(s.pathToDataLib || './backend/libs/data/src/'),
110
110
  cypressPath: (0, types_1.toPath)(s.pathToCypress || './e2e/cypress/'),
111
- e2eLibPath: (0, types_1.toPath)(s.pathToE2ELib || './e2e/src/'),
112
- importExportPath: (0, types_1.toPath)(s.pathToImportExport || 'import-export'),
113
- actionsPath: (0, types_1.toPath)(s.pathToActions || 'actions'),
114
- businessLogicPath: (0, types_1.toPath)(s.pathToBusinessLogic || 'business-logic'),
115
- migrationsFolderPath: (0, types_1.toPath)(s.migrationsFolder || 'migrations'),
116
- modelTypeDefinitionsPath: (0, types_1.toPath)(s.pathToTypes || 'types'),
117
- reactFolderPath: (0, types_1.toPath)(s.reactFolderOutput || 'react'),
118
- seedDataPath: (0, types_1.toPath)(s.pathToSeedData || 'seed-data'),
119
- seedLibPath: (0, types_1.toPath)(s.pathToSeedLib || 'seed'),
120
- trpcRoutesFolderPath: (0, types_1.toPath)(s.trpcRoutesFolder || 'trpc'),
111
+ e2eLibPath: (0, types_1.toPath)(s.pathToE2ELib || './backend/libs/e2e/src/'),
112
+ importExportPath: (0, types_1.toPath)(s.pathToImportExport || './backend/libs/import-export/src/'),
113
+ actionsPath: (0, types_1.toPath)(s.pathToActions || './backend/libs/actions/src/'),
114
+ businessLogicPath: (0, types_1.toPath)(s.pathToBusinessLogic || './backend/libs/business-logic/src/'),
115
+ prismaMigrationsFolderPath: (0, types_1.toPath)(s.prismaMigrationsFolder || './migrations'),
116
+ modelTypeDefinitionsPath: (0, types_1.toPath)(s.pathToTypes || './backend/libs/types/src/'),
117
+ reactFolderPath: (0, types_1.toPath)(s.reactFolderOutput || './web/src/components/'),
118
+ seedDataPath: (0, types_1.toPath)(s.pathToSeedData || './backend/seed-data/src/'),
119
+ seedLibPath: (0, types_1.toPath)(s.pathToSeedLib || './backend/libs/seed/src/'),
120
+ trpcRoutesFolderPath: (0, types_1.toPath)(s.trpcRoutesFolder || './backend/libs/trpc/src/routes/'),
121
121
  },
122
122
  randomSeed: s.randomSeed,
123
123
  force: s.force,
124
- disableGenerators: {
125
- actions: s.pathToActions === undefined,
126
- businessLogic: s.pathToBusinessLogic === undefined,
127
- data: s.pathToDataLib === undefined,
128
- importExport: s.pathToImportExport === undefined,
129
- react: s.reactFolderOutput === undefined,
130
- seed: s.pathToSeedLib === undefined,
131
- trpc: s.trpcRoutesFolder === undefined,
132
- types: s.pathToTypes === undefined,
133
- },
134
124
  userType: (0, types_1.toTypeName)(`User`),
135
125
  };
136
126
  });
@@ -173,100 +163,75 @@ function generate({ models, enums, config, prismaClientPath, logger, }) {
173
163
  for (const model of models) {
174
164
  const meta = (0, meta_1.getModelMetadata)({ model });
175
165
  // Types
176
- if (!config.disableGenerators.types) {
177
- generated.write(`/${meta.types.filePath}.ts`, (0, types_generator_3.generateModelTypes)({ model, meta }));
178
- }
166
+ generated.write(`/${meta.types.filePath}.ts`, (0, types_generator_3.generateModelTypes)({ model, meta }));
179
167
  // Seed
180
- if (!config.disableGenerators.seed) {
181
- generated.write(`/${meta.seed.filePath}.ts`, (0, seed_generator_1.generateSeedModel)({ model, itemCount: 5, meta }));
182
- }
168
+ generated.write(`/${meta.seed.filePath}.ts`, (0, seed_generator_1.generateSeedModel)({ model, itemCount: 5, meta }));
183
169
  // Data
184
- if (!config.disableGenerators.data) {
185
- generated.write(`/${meta.data.stubFilePath}.ts`, (0, stub_generator_1.generateStub)({ model, meta }));
186
- generated.write(`/${meta.data.repository.filePath}.ts`, (0, repository_generator_1.generateRepository)({ model, meta }));
187
- generated.write(`/${meta.data.mockRepository.filePath}.ts`, (0, repository_generator_1.generateMockRepository)({ model, meta }));
188
- generated.write(`/${meta.e2e.dataMockerStubFilePath}.ts`, (0, stub_generator_1.generateStub)({ model, meta }));
189
- }
170
+ generated.write(`/${meta.data.stubFilePath}.ts`, (0, stub_generator_1.generateStub)({ model, meta }));
171
+ generated.write(`/${meta.data.repository.filePath}.ts`, (0, repository_generator_1.generateRepository)({ model, meta }));
172
+ generated.write(`/${meta.data.mockRepository.filePath}.ts`, (0, repository_generator_1.generateMockRepository)({ model, meta }));
173
+ generated.write(`/${meta.e2e.dataMockerStubFilePath}.ts`, (0, stub_generator_1.generateStub)({ model, meta }));
190
174
  // Import-Export
191
- if (!config.disableGenerators.importExport) {
192
- generated.write(`/${meta.importExport.decoder.filePath}.ts`, (0, importexport_decoder_generator_1.generateModelImportExportDecoder)({ model, meta, schemaMeta: (0, meta_1.getSchemaMetadata)({ config }) }));
193
- }
175
+ generated.write(`/${meta.importExport.decoder.filePath}.ts`, (0, importexport_decoder_generator_1.generateModelImportExportDecoder)({ model, meta, schemaMeta: (0, meta_1.getSchemaMetadata)({ config }) }));
194
176
  // Business Logic
195
- if (!config.disableGenerators.businessLogic) {
196
- generated.write(`/${meta.businessLogic.view.serviceFilePath}.ts`, (0, businesslogic_view_generator_1.generateModelBusinessLogicView)({ model, meta }));
197
- generated.write(`/${meta.businessLogic.update.serviceFilePath}.ts`, (0, businesslogic_update_generator_1.generateModelBusinessLogicUpdate)({ model, meta }));
198
- }
177
+ generated.write(`/${meta.businessLogic.view.serviceFilePath}.ts`, (0, businesslogic_view_generator_1.generateModelBusinessLogicView)({ model, meta }));
178
+ generated.write(`/${meta.businessLogic.update.serviceFilePath}.ts`, (0, businesslogic_update_generator_1.generateModelBusinessLogicUpdate)({ model, meta }));
199
179
  // Routes
200
- if (!config.disableGenerators.trpc) {
201
- generated.write(`/${meta.trpc.routerFilePath}.ts`, (0, route_generator_1.generateRoute)({ model, meta }));
202
- }
180
+ generated.write(`/${meta.trpc.routerFilePath}.ts`, (0, route_generator_1.generateRoute)({ model, meta }));
203
181
  // React
204
- if (!config.disableGenerators.react) {
205
- yield generated.copy((0, react_generator_2.generateReactComponentsForModel)({ model, meta }), meta.react.folderPath);
206
- }
182
+ yield generated.copy((0, react_generator_2.generateReactComponentsForModel)({ model, meta }), meta.react.folderPath);
207
183
  logger.log(`- ${model.name} processed`);
208
184
  }
209
185
  // Generate Enums
210
186
  for (const enumerator of enums.values()) {
211
187
  const meta = (0, meta_1.getEnumMetadata)({ enumerator });
212
- if (!config.disableGenerators.types) {
213
- generated.write(`/${meta.types.filePath}.ts`, (0, types_generator_1.generateEnumType)({ enumerator, prismaClientPath, meta }));
214
- }
215
- if (!config.disableGenerators.react) {
216
- yield generated.copy((0, react_generator_1.generateEnumReactComponents)({ enumerator, meta }), meta.react.folderPath);
217
- }
188
+ generated.write(`/${meta.types.filePath}.ts`, (0, types_generator_1.generateEnumType)({ enumerator, prismaClientPath, meta }));
189
+ yield generated.copy((0, react_generator_1.generateEnumReactComponents)({ enumerator, meta }), meta.react.folderPath);
218
190
  }
219
191
  // Generate Index Files and Services
220
192
  const meta = (0, meta_1.getSchemaMetadata)({ config });
221
- if (!config.disableGenerators.data) {
222
- generated.write(`/${meta.data.dataMockModuleFilePath}.ts`, (0, datamock_module_generator_1.generateDataMockModule)({ models, meta }));
223
- generated.write(`/${meta.data.dataModuleFilePath}.ts`, (0, datamodule_generator_1.generateDataModule)({ models, meta }));
224
- generated.write(`/${meta.data.dataService.filePath}.ts`, (0, dataservice_generator_1.generateDataService)({ models, meta }));
225
- generated.write(`/${meta.data.testDataServiceFilePath}.ts`, (0, testdata_service_generator_1.generateTestDataService)({ models, meta }));
226
- generated.write(`/${meta.e2e.dataMocker.filePath}.ts`, (0, datamocker_generator_1.generateDataMocker)({ models, meta }));
227
- generated.write(`/${meta.e2e.selectorsFilePath}.ts`, (0, selectors_generator_1.generateSelectors)());
228
- generated.write(`/${meta.e2e.dataMocker.stubIndexFilePath}.ts`, (0, stubs_generator_1.generateDataMockerStubsIndex)({ models, meta }));
229
- generated.write(`/${meta.data.repository.constFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesArray)({ models, meta }));
230
- generated.write(`/${meta.data.repository.indexFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesIndex)({ models, meta }));
231
- generated.write(`/${meta.data.stubIndexFilePath}.ts`, (0, stubs_generator_1.generateStubsIndex)({ models, meta }));
232
- generated.write(`/${meta.data.types.filePath}.ts`, (0, data_types_generator_1.generateDataTypes)({ models, meta }));
233
- // We only generate the empty database migration if the migration folder already has an existing migration
234
- // Else we would generate a migration that deletes from tables that have not yet been created in the database
235
- // We include this check here as the template does not come with any migration - hence this migration should also not be generated
236
- if ((0, emptydatabasemigration_generator_1.prismaMigrationExists)(meta)) {
237
- generated.write((0, emptydatabasemigration_generator_1.deriveEmptyDatabaseMigrationFilePath)(meta), (0, emptydatabasemigration_generator_1.generateEmptyDatabaseStoredProcedure)({ models, meta }));
238
- }
239
- }
240
- if (!config.disableGenerators.importExport) {
241
- generated.write(`/${meta.importExport.types.filePath}.ts`, (0, importexport_types_generator_1.generateImportExportTypes)({ models, meta }));
242
- generated.write(`/${meta.importExport.exporterClass.filePath}.ts`, (0, importexport_exporter_class_generator_1.generateImportExportExporterClass)({ models, meta }));
243
- generated.write(`/${meta.importExport.importService.filePath}.ts`, (0, importexport_import_service_generator_1.generateImportExportImportService)({ models, meta }));
244
- generated.write(`/${meta.importExport.decoder.indexFilePath}.ts`, (0, importexport_decoder_generator_1.generateImportExportDecoderIndex)({ models, meta }));
245
- generated.write(`/${meta.importExport.decoder.fullDecoderFilePath}.ts`, (0, importexport_decoder_generator_1.generateImportExportDecoder)({ models, meta }));
246
- generated.write(`/${meta.importExport.converterFunctions.filePath}.ts`, (0, importexport_convert_import_functions_generator_1.generateImportExportConvertImportFunctions)({ models, meta }));
247
- }
248
- if (!config.disableGenerators.actions) {
249
- generated.write(`/${meta.actions.dispatcherService.filePath}.ts`, (0, dispatcher_service_generator_1.generateActionsDispatcherService)({ models, meta }));
250
- }
251
- if (!config.disableGenerators.businessLogic) {
252
- generated.write(`/${meta.businessLogic.view.indexFilePath}.ts`, (0, businesslogic_view_index_generator_1.generateBusinessLogicViewIndex)({ models, meta }));
253
- generated.write(`/${meta.businessLogic.view.moduleFilePath}.ts`, (0, businesslogic_view_module_generator_1.generateBusinessLogicViewModule)({ models, meta }));
254
- generated.write(`/${meta.businessLogic.view.serviceFilePath}.ts`, (0, businesslogic_view_service_generator_1.generateBusinessLogicViewService)({ models, meta }));
255
- generated.write(`/${meta.businessLogic.update.indexFilePath}.ts`, (0, businesslogic_update_index_generator_1.generateBusinessLogicUpdateIndex)({ models, meta }));
256
- generated.write(`/${meta.businessLogic.update.moduleFilePath}.ts`, (0, businesslogic_update_module_generator_1.generateBusinessLogicUpdateModule)({ models, meta }));
257
- generated.write(`/${meta.businessLogic.update.serviceFilePath}.ts`, (0, businesslogic_update_service_generator_1.generateBusinessLogicUpdateService)({ models, meta }));
258
- generated.write(`/${meta.businessLogic.update.actionTypesFilePath}.ts`, (0, businesslogic_actiontypes_generator_1.generateBusinessLogicActionTypes)({ models, meta }));
259
- }
260
- if (!config.disableGenerators.seed) {
261
- generated.write(`/${meta.seedData.initialMigrationFilePath}.ts`, (0, seed_migration_generator_1.generateSeedMigration)({ models, meta }));
262
- generated.write(`/${meta.seedData.templateExcelFilePath}`, yield (0, seed_template_generator_1.generateSeedExcelTemplate)({ models }));
263
- }
264
- if (!config.disableGenerators.trpc) {
265
- generated.write(`/${meta.trpc.routesFilePath}.ts`, (0, route_generator_1.generateRoutesIndex)({ models, meta }));
266
- }
267
- if (!config.disableGenerators.types) {
268
- generated.write(`/${meta.types.indexFilePath}.ts`, (0, types_generator_2.generateTypesIndex)({ models, enums, meta }));
193
+ // Data
194
+ generated.write(`/${meta.data.dataMockModuleFilePath}.ts`, (0, datamock_module_generator_1.generateDataMockModule)({ models, meta }));
195
+ generated.write(`/${meta.data.dataModuleFilePath}.ts`, (0, datamodule_generator_1.generateDataModule)({ models, meta }));
196
+ generated.write(`/${meta.data.dataService.filePath}.ts`, (0, dataservice_generator_1.generateDataService)({ models, meta }));
197
+ generated.write(`/${meta.data.testDataServiceFilePath}.ts`, (0, testdata_service_generator_1.generateTestDataService)({ models, meta }));
198
+ generated.write(`/${meta.e2e.dataMocker.filePath}.ts`, (0, datamocker_generator_1.generateDataMocker)({ models, meta }));
199
+ generated.write(`/${meta.e2e.selectorsFilePath}.ts`, (0, selectors_generator_1.generateSelectors)());
200
+ generated.write(`/${meta.e2e.dataMocker.stubIndexFilePath}.ts`, (0, stubs_generator_1.generateDataMockerStubsIndex)({ models, meta }));
201
+ generated.write(`/${meta.data.repository.constFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesArray)({ models, meta }));
202
+ generated.write(`/${meta.data.repository.indexFilePath}.ts`, (0, repositories_generator_1.generateRepositoriesIndex)({ models, meta }));
203
+ generated.write(`/${meta.data.stubIndexFilePath}.ts`, (0, stubs_generator_1.generateStubsIndex)({ models, meta }));
204
+ generated.write(`/${meta.data.types.filePath}.ts`, (0, data_types_generator_1.generateDataTypes)({ models, meta }));
205
+ // We only generate the empty database migration if the migration folder already has an existing migration
206
+ // Else we would generate a migration that deletes from tables that have not yet been created in the database
207
+ // We include this check here as the template does not come with any migration - hence this migration should also not be generated
208
+ if ((0, emptydatabasemigration_generator_1.prismaMigrationExists)(meta)) {
209
+ generated.write((0, emptydatabasemigration_generator_1.deriveEmptyDatabaseMigrationFilePath)(meta), (0, emptydatabasemigration_generator_1.generateEmptyDatabaseStoredProcedure)({ models, meta }));
269
210
  }
211
+ // Import-Export
212
+ generated.write(`/${meta.importExport.types.filePath}.ts`, (0, importexport_types_generator_1.generateImportExportTypes)({ models, meta }));
213
+ generated.write(`/${meta.importExport.exporterClass.filePath}.ts`, (0, importexport_exporter_class_generator_1.generateImportExportExporterClass)({ models, meta }));
214
+ generated.write(`/${meta.importExport.importService.filePath}.ts`, (0, importexport_import_service_generator_1.generateImportExportImportService)({ models, meta }));
215
+ generated.write(`/${meta.importExport.decoder.indexFilePath}.ts`, (0, importexport_decoder_generator_1.generateImportExportDecoderIndex)({ models, meta }));
216
+ generated.write(`/${meta.importExport.decoder.fullDecoderFilePath}.ts`, (0, importexport_decoder_generator_1.generateImportExportDecoder)({ models, meta }));
217
+ generated.write(`/${meta.importExport.converterFunctions.filePath}.ts`, (0, importexport_convert_import_functions_generator_1.generateImportExportConvertImportFunctions)({ models, meta }));
218
+ // Actions
219
+ generated.write(`/${meta.actions.dispatcherService.filePath}.ts`, (0, dispatcher_service_generator_1.generateActionsDispatcherService)({ models, meta }));
220
+ // Business Logic
221
+ generated.write(`/${meta.businessLogic.view.indexFilePath}.ts`, (0, businesslogic_view_index_generator_1.generateBusinessLogicViewIndex)({ models, meta }));
222
+ generated.write(`/${meta.businessLogic.view.moduleFilePath}.ts`, (0, businesslogic_view_module_generator_1.generateBusinessLogicViewModule)({ models, meta }));
223
+ generated.write(`/${meta.businessLogic.view.serviceFilePath}.ts`, (0, businesslogic_view_service_generator_1.generateBusinessLogicViewService)({ models, meta }));
224
+ generated.write(`/${meta.businessLogic.update.indexFilePath}.ts`, (0, businesslogic_update_index_generator_1.generateBusinessLogicUpdateIndex)({ models, meta }));
225
+ generated.write(`/${meta.businessLogic.update.moduleFilePath}.ts`, (0, businesslogic_update_module_generator_1.generateBusinessLogicUpdateModule)({ models, meta }));
226
+ generated.write(`/${meta.businessLogic.update.serviceFilePath}.ts`, (0, businesslogic_update_service_generator_1.generateBusinessLogicUpdateService)({ models, meta }));
227
+ generated.write(`/${meta.businessLogic.update.actionTypesFilePath}.ts`, (0, businesslogic_actiontypes_generator_1.generateBusinessLogicActionTypes)({ models, meta }));
228
+ // Seed
229
+ generated.write(`/${meta.seedData.initialMigrationFilePath}.ts`, (0, seed_migration_generator_1.generateSeedMigration)({ models, meta }));
230
+ generated.write(`/${meta.seedData.templateExcelFilePath}`, yield (0, seed_template_generator_1.generateSeedExcelTemplate)({ models }));
231
+ // Routes
232
+ generated.write(`/${meta.trpc.routesFilePath}.ts`, (0, route_generator_1.generateRoutesIndex)({ models, meta }));
233
+ // Types
234
+ generated.write(`/${meta.types.indexFilePath}.ts`, (0, types_generator_2.generateTypesIndex)({ models, enums, meta }));
270
235
  // -------------------------------------------------------------------------
271
236
  // Add disclaimer and format.
272
237
  yield generated.transformUTF8Files((path, content) => `${DISCLAIMER}\n${content}`);
@@ -45,11 +45,11 @@ exports.generateEmptyDatabaseStoredProcedure = generateEmptyDatabaseStoredProced
45
45
  function deriveEmptyDatabaseMigrationFilePath(meta) {
46
46
  const firstMigration = getFirstPrismaMigration(meta);
47
47
  if (firstMigration === undefined) {
48
- throw new Error(`No Prisma migration found in ${meta.migrationsPath}! Please run "prisma migrate dev" first.`);
48
+ throw new Error(`No Prisma migration found in ${meta.prismaMigrationsPath}! Please run "prisma migrate dev" first.`);
49
49
  }
50
50
  const existingTimestamp = firstMigration.split('_')[0];
51
51
  const nextTimestamp = (Number.parseInt(existingTimestamp) + 1).toString();
52
- return `/${meta.migrationsPath}/${nextTimestamp}_emptyDatabase/migration.sql`;
52
+ return `/${meta.prismaMigrationsPath}/${nextTimestamp}_emptyDatabase/migration.sql`;
53
53
  }
54
54
  exports.deriveEmptyDatabaseMigrationFilePath = deriveEmptyDatabaseMigrationFilePath;
55
55
  /**
@@ -64,7 +64,7 @@ exports.prismaMigrationExists = prismaMigrationExists;
64
64
  * Returns the first migration in the migrations folder if it exists.
65
65
  */
66
66
  function getFirstPrismaMigration(meta) {
67
- const folder = meta.migrationsPath;
67
+ const folder = meta.prismaMigrationsPath;
68
68
  try {
69
69
  return fs_1.default.readdirSync(folder).find((f) => fs_1.default.statSync(`${folder}/${f}`).isDirectory());
70
70
  }
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.generateModelLookupComponents = void 0;
4
+ const id_collector_1 = require("../../../lib/id-collector");
4
5
  const imports_1 = require("../../../lib/imports");
5
6
  /**
6
7
  * Utility generator that generates lookup components for a given model.
7
8
  */
8
9
  function generateModelLookupComponents({ model, meta }) {
9
10
  const { react: { context, components }, } = meta;
11
+ const selectorCollector = id_collector_1.SelectorCollector.from(meta.seed.constantName + '-formComponents');
10
12
  const imports = imports_1.ImportsGenerator.from(meta.react.folderPath)
11
13
  .addImport({
12
14
  items: [context.hookFnName],
@@ -26,7 +28,7 @@ function generateModelLookupComponents({ model, meta }) {
26
28
  * ${model.description}
27
29
  */`;
28
30
  }
29
- return `
31
+ return /* ts */ `
30
32
  import React from 'react'
31
33
 
32
34
  import { MenuSelectInput, MenuSelectField } from '@components/atoms/MenuSelect'
@@ -46,7 +48,16 @@ export const ${components.forms.selectInputName} = ({
46
48
  ...delegated
47
49
  }: UnionOmit<React.ComponentPropsWithoutRef<typeof SelectInput<${typeName}>>, ${tsOmittedFields}>) => {
48
50
  const { list, ready } = ${context.hookFnName}()
49
- return <SelectInput<${typeName}> options={list} ${reactLabelField} loading={!ready} {...delegated} />
51
+ return <SelectInput<${typeName}>
52
+ options={list}
53
+ ${reactLabelField}
54
+ loading={!ready}
55
+ __cypress_field_selector__={
56
+ delegated.__cypress_field_selector__
57
+ ?? "${selectorCollector.idFor('', { typePrefix: 'selectInput' })}"
58
+ }
59
+ {...delegated}
60
+ />
50
61
  }
51
62
 
52
63
  ${description}
@@ -54,7 +65,16 @@ export const ${components.forms.selectFieldName} = ({
54
65
  ...delegated
55
66
  }: Omit<React.ComponentPropsWithoutRef<typeof SelectField<${typeName}>>, ${tsOmittedFields}>) => {
56
67
  const { list, ready } = ${context.hookFnName}()
57
- return <SelectField<${typeName}> options={list} ${reactLabelField} loading={!ready} {...delegated} />
68
+ return <SelectField<${typeName}>
69
+ options={list}
70
+ ${reactLabelField}
71
+ loading={!ready}
72
+ __cypress_field_selector__={
73
+ delegated.__cypress_field_selector__
74
+ ?? "${selectorCollector.idFor('', { typePrefix: 'selectField' })}"
75
+ }
76
+ {...delegated}
77
+ />
58
78
  }
59
79
 
60
80
  // Menu Select
@@ -64,7 +84,16 @@ export const ${components.forms.menuSelectInputName} = ({
64
84
  ...delegated
65
85
  }: UnionOmit<React.ComponentPropsWithoutRef<typeof MenuSelectInput<${typeName}>>, ${tsOmittedFields}>) => {
66
86
  const { list, ready } = ${context.hookFnName}()
67
- return <MenuSelectInput<${typeName}> options={list} ${reactLabelField} loading={!ready} {...delegated} />
87
+ return <MenuSelectInput<${typeName}>
88
+ options={list}
89
+ ${reactLabelField}
90
+ loading={!ready}
91
+ __cypress_options_selector__={
92
+ delegated.__cypress_options_selector__
93
+ ?? "${selectorCollector.idFor('', { typePrefix: 'menuSelectInput' })}"
94
+ }
95
+ {...delegated}
96
+ />
68
97
  }
69
98
 
70
99
  ${description}
@@ -72,7 +101,16 @@ export const ${components.forms.menuSelectFieldName} = ({
72
101
  ...delegated
73
102
  }: UnionOmit<React.ComponentPropsWithoutRef<typeof MenuSelectField<${typeName}>>, ${tsOmittedFields}>) => {
74
103
  const { list, ready } = ${context.hookFnName}()
75
- return <MenuSelectField<${typeName}> options={list} ${reactLabelField} loading={!ready} {...delegated} />
104
+ return <MenuSelectField<${typeName}>
105
+ options={list}
106
+ ${reactLabelField}
107
+ loading={!ready}
108
+ __cypress_options_selector__={
109
+ delegated.__cypress_options_selector__
110
+ ?? "${selectorCollector.idFor('', { typePrefix: 'menuSelectField' })}"
111
+ }
112
+ {...delegated}
113
+ />
76
114
  }
77
115
 
78
116
  // Search
@@ -82,7 +120,20 @@ export const ${components.forms.searchInputName} = ({
82
120
  ...delegated
83
121
  }: UnionOmit<React.ComponentPropsWithoutRef<typeof SearchInput<${typeName}>>, ${tsOmittedFields}>) => {
84
122
  const { list, ready } = ${context.hookFnName}()
85
- return <SearchInput<${typeName}> options={list} ${reactLabelField} loading={!ready} {...delegated} />
123
+ return <SearchInput<${typeName}>
124
+ options={list}
125
+ ${reactLabelField}
126
+ loading={!ready}
127
+ __cypress_combobox_selector__={
128
+ delegated.__cypress_combobox_selector__
129
+ ?? "${selectorCollector.idFor('field', { typePrefix: 'searchInput' })}"
130
+ }
131
+ __cypress_options_selector__={
132
+ delegated.__cypress_options_selector__
133
+ ?? "${selectorCollector.idFor('options', { typePrefix: 'searchInput' })}"
134
+ }
135
+ {...delegated}
136
+ />
86
137
  }
87
138
 
88
139
  ${description}
@@ -90,17 +141,38 @@ export const ${components.forms.searchFieldName} = ({
90
141
  ...delegated
91
142
  }: Omit<React.ComponentPropsWithoutRef<typeof SearchField<${typeName}>>, ${tsOmittedFields}>) => {
92
143
  const { list, ready } = ${context.hookFnName}()
93
- return <SearchField<${typeName}> options={list} ${reactLabelField} loading={!ready} {...delegated} />
144
+ return <SearchField<${typeName}>
145
+ options={list}
146
+ ${reactLabelField}
147
+ loading={!ready}
148
+ __cypress_combobox_selector__={
149
+ delegated.__cypress_combobox_selector__
150
+ ?? "${selectorCollector.idFor('field', { typePrefix: 'searchField' })}"
151
+ }
152
+ __cypress_options_selector__={
153
+ delegated.__cypress_options_selector__
154
+ ?? "${selectorCollector.idFor('options', { typePrefix: 'searchField' })}"
155
+ }
156
+ {...delegated}
157
+ />
94
158
  }
95
159
 
96
160
  // Table
97
-
98
161
  ${description}
99
162
  export const ${components.forms.tableSelectInputName} = ({
100
163
  ...delegated
101
164
  }: UnionOmit<React.ComponentPropsWithoutRef<typeof TableSelectInput<${typeName}>>, ${tsOmittedFields}>) => {
102
165
  const { list, ready } = ${context.hookFnName}()
103
- return <TableSelectInput<${typeName}> options={list} ${reactLabelField} loading={!ready} {...delegated} />
166
+ return <TableSelectInput<${typeName}>
167
+ options={list}
168
+ ${reactLabelField}
169
+ loading={!ready}
170
+ __cypress_input_field_selector__={
171
+ delegated.__cypress_input_field_selector__
172
+ ?? "${selectorCollector.idFor('', { typePrefix: 'tableSelectInput' })}"
173
+ }
174
+ {...delegated}
175
+ />
104
176
  }
105
177
 
106
178
  ${description}
@@ -108,7 +180,16 @@ export const ${components.forms.tableSelectFieldName} = ({
108
180
  ...delegated
109
181
  }: UnionOmit<React.ComponentPropsWithoutRef<typeof TableSelectField<${typeName}>>, ${tsOmittedFields}>) => {
110
182
  const { list, ready } = ${context.hookFnName}()
111
- return <TableSelectField<${typeName}> options={list} ${reactLabelField} loading={!ready} {...delegated} />
183
+ return <TableSelectField<${typeName}>
184
+ options={list}
185
+ ${reactLabelField}
186
+ loading={!ready}
187
+ __cypress_input_field_selector__={
188
+ delegated.__cypress_input_field_selector__
189
+ ?? "${selectorCollector.idFor('', { typePrefix: 'tableSelectField' })}"
190
+ }
191
+ {...delegated}
192
+ />
112
193
  }
113
194
  `;
114
195
  }
@@ -785,10 +785,6 @@ function generateUserRepositorySpecificBlocks_InDatabase({ model, meta, imports,
785
785
  rootUserInitializeBlock: '',
786
786
  };
787
787
  }
788
- imports.addImport({
789
- from: meta.types.importPath,
790
- items: [(0, types_1.toVariableName)('UserRole')],
791
- });
792
788
  const { rootUserId, rootUserValue } = generateSharedRootUserBlocks({ model, meta, imports });
793
789
  return {
794
790
  rootUserNameConst: `public static ROOT_USER_ID = ${meta.types.toBrandedIdTypeFnName}(${rootUserId})`,
@@ -868,7 +864,7 @@ function generateSharedRootUserBlocks({ model, meta, imports, }) {
868
864
  }
869
865
  else if (field.kind === 'id') {
870
866
  if (field.unbrandedTypeName === 'string') {
871
- value = "rootId";
867
+ value = 'rootId';
872
868
  }
873
869
  else if (field.unbrandedTypeName === 'number') {
874
870
  value = -1;
@@ -635,9 +635,9 @@ export type SchemaMetaData = {
635
635
  selectorsFilePath: Types.FilePath;
636
636
  };
637
637
  /**
638
- * Path to the directory containing migrations.
638
+ * Path to the directory containing Prisma migrations.
639
639
  */
640
- migrationsPath: Types.FilePath;
640
+ prismaMigrationsPath: Types.FilePath;
641
641
  /**
642
642
  * The schema configuration for reference.
643
643
  */
package/dist/lib/meta.js CHANGED
@@ -251,7 +251,7 @@ function getSchemaMetadata({ config }) {
251
251
  upsert: Types.toTypeName(`UpsertDTO`),
252
252
  },
253
253
  },
254
- migrationsPath: Types.toPath(`${config.paths.migrationsFolderPath}`),
254
+ prismaMigrationsPath: Types.toPath(`${config.paths.prismaMigrationsFolderPath}`),
255
255
  config,
256
256
  };
257
257
  }
@@ -7,43 +7,6 @@ import * as Types from './types';
7
7
  * NOTE: This may be accessed in every model, field and enumerator.
8
8
  */
9
9
  export type SchemaConfig = {
10
- /**
11
- * Indicates whether the generator should be ignored
12
- */
13
- disableGenerators: {
14
- /**
15
- * If true, actions module will not be generated.
16
- */
17
- actions: boolean;
18
- /**
19
- * If true, business logic module will not be generated.
20
- */
21
- businessLogic: boolean;
22
- /**
23
- * If true, data module will not be generated.
24
- */
25
- data: boolean;
26
- /**
27
- * If true, import-export module will not be generated.
28
- */
29
- importExport: boolean;
30
- /**
31
- * If true, seed data will not be generated.
32
- */
33
- seed: boolean;
34
- /**
35
- * If true, React components will not be generated.
36
- */
37
- react: boolean;
38
- /**
39
- * If true, trpc routes will not be generated.
40
- */
41
- trpc: boolean;
42
- /**
43
- * If true, type definitions for models will not be generated.
44
- */
45
- types: boolean;
46
- };
47
10
  paths: {
48
11
  /**
49
12
  * Path to the directory containing actions.
@@ -84,7 +47,7 @@ export type SchemaConfig = {
84
47
  /**
85
48
  * Path to the directory containing Prisma migrations.
86
49
  */
87
- migrationsFolderPath: Types.FilePath;
50
+ prismaMigrationsFolderPath: Types.FilePath;
88
51
  /**
89
52
  * Path to the directory containing model type definitions.
90
53
  *
@@ -3,3 +3,7 @@
3
3
  * By showing the error via console.log and throwing it again, we ensure that the error message is shown.
4
4
  */
5
5
  export declare function throwError(message: string): never;
6
+ /**
7
+ * Extracts the error message from an error object or any other object.
8
+ */
9
+ export declare function extractError(error: unknown): string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.throwError = void 0;
3
+ exports.extractError = exports.throwError = void 0;
4
4
  /**
5
5
  * Prisma generator often overwrites any error message because of some concurrency issues.
6
6
  * By showing the error via console.log and throwing it again, we ensure that the error message is shown.
@@ -11,3 +11,13 @@ function throwError(message) {
11
11
  throw new Error(m);
12
12
  }
13
13
  exports.throwError = throwError;
14
+ /**
15
+ * Extracts the error message from an error object or any other object.
16
+ */
17
+ function extractError(error) {
18
+ if (error instanceof Error) {
19
+ return error.message;
20
+ }
21
+ return JSON.stringify(error);
22
+ }
23
+ exports.extractError = extractError;
@@ -37,6 +37,7 @@ const REQUIRED_MODELS = ['User', 'Config', 'File', 'Action', 'Mutation'];
37
37
  */
38
38
  function parsePrismaSchema({ datamodel: { enums: enumsRaw, models: modelsRaw }, config, }) {
39
39
  ensureRequiredModelsExists(modelsRaw);
40
+ ensureConsistency({ models: modelsRaw, enums: enumsRaw });
40
41
  // NOTE: We preprocess models and enums so that we can populate relationships.
41
42
  const models = modelsRaw.map((dmmfModel) => parseModelCore({ dmmfModel, config }));
42
43
  const enums = enumsRaw.map((dmmfEnum) => parseEnum({ dmmfEnum, config }));
@@ -49,10 +50,60 @@ exports.parsePrismaSchema = parsePrismaSchema;
49
50
  function ensureRequiredModelsExists(models) {
50
51
  for (const requiredModel of REQUIRED_MODELS) {
51
52
  if (!models.find((m) => m.name === requiredModel)) {
52
- (0, error_1.throwError)(`Required model ${requiredModel} not found in schema!`);
53
+ (0, error_1.throwError)(`Required model ${highlight(requiredModel)} not found in schema!`);
53
54
  }
54
55
  }
55
56
  }
57
+ /**
58
+ * Validates:
59
+ * - That there are no duplicate model names
60
+ * - That model names are singular
61
+ * - That model attributes are valid
62
+ * - That field attributes are valid
63
+ * - That enum attributes are valid
64
+ */
65
+ function ensureConsistency({ models, enums }) {
66
+ const errors = [];
67
+ const modelNames = models.map((m) => m.name);
68
+ const duplicateModelName = modelNames.find((name, i) => modelNames.indexOf(name) !== i);
69
+ if (duplicateModelName) {
70
+ errors.push(`Model ${duplicateModelName} is defined more than once.`);
71
+ }
72
+ for (const model of models) {
73
+ if ((0, string_1.isPlural)(model.name)) {
74
+ errors.push(`Model ${highlight(model.name)} is plural. Please use singular names for models.`);
75
+ }
76
+ try {
77
+ (0, attributes_1.getModelAttributes)(model);
78
+ }
79
+ catch (e) {
80
+ errors.push(`Model ${highlight(model.name)} has invalid model attributes: ${(0, error_1.extractError)(e)}`);
81
+ }
82
+ }
83
+ for (const model of models) {
84
+ for (const field of model.fields) {
85
+ try {
86
+ (0, attributes_1.getFieldAttributes)(field);
87
+ }
88
+ catch (e) {
89
+ errors.push(`Model ${highlight(model.name)} has invalid attributes for field ${highlight(field.name)}:
90
+ ${(0, error_1.extractError)(e)}`);
91
+ }
92
+ }
93
+ }
94
+ for (const enumDef of enums) {
95
+ try {
96
+ (0, attributes_1.getEnumAttributes)(enumDef);
97
+ }
98
+ catch (e) {
99
+ errors.push(`Enum ${highlight(enumDef.name)} has invalid attributes:
100
+ ${(0, error_1.extractError)(e)}`);
101
+ }
102
+ }
103
+ if (errors.length > 0) {
104
+ (0, error_1.throwError)(`${errors.length} ${(0, string_1.pluralize)('issue', errors.length)} detected in schema:\n * ${errors.join('\n * ')}`);
105
+ }
106
+ }
56
107
  function isModelNotIgnored(model) {
57
108
  return model !== undefined && !model.attributes.ignore;
58
109
  }
@@ -98,14 +149,14 @@ function parseModel({ dmmfModel, enums, models, config, }) {
98
149
  continue;
99
150
  }
100
151
  if (dmmfField.relationFromFields.length > 1) {
101
- throw new Error(`❌❌❌ Relation ${dmmfField.relationName} has more than one from field`);
152
+ (0, error_1.throwError)(`Field ${highlight(`${dmmfModel.name}.${dmmfField.relationName}`)} has more than one "from" field`);
102
153
  }
103
154
  const referencedModel = models.find((m) => m.sourceName === dmmfField.type);
104
155
  if (!referencedModel) {
105
- (0, error_1.throwError)(`Investigate: Field references unknown model ${dmmfField.type}.`);
156
+ (0, error_1.throwError)(`Field ${highlight(`${dmmfModel.name}.${dmmfField.name}`)} references unknown model ${highlight(dmmfField.type)}.`);
106
157
  }
107
158
  if (dmmfField.relationOnDelete && dmmfField.relationOnDelete !== 'NoAction') {
108
- (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!`);
159
+ (0, error_1.throwError)(`Investigate model ${highlight(dmmfModel.name)}: "onDelete" attribute for relationship ${highlight(dmmfField.relationName)} must be "NoAction": any deletes must be handled in the application layer, e.g. to update repository and search caches!`);
109
160
  }
110
161
  relations[dmmfField.relationFromFields[0]] = referencedModel;
111
162
  }
@@ -123,6 +174,7 @@ function parseModel({ dmmfModel, enums, models, config, }) {
123
174
  dmmfField.kind !== 'object')
124
175
  .map((dmmfField) => {
125
176
  const attributes = (0, attributes_1.getFieldAttributes)(dmmfField);
177
+ const fieldName = highlight(`${dmmfModel.name}.${dmmfField.name}`);
126
178
  const shared = {
127
179
  name: Types.toFieldName((0, string_1.toCamelCase)(dmmfField.name)),
128
180
  description: attributes.description,
@@ -137,7 +189,7 @@ function parseModel({ dmmfModel, enums, models, config, }) {
137
189
  const refModel = relations[dmmfField.name];
138
190
  const refField = relationFields[dmmfField.name];
139
191
  if (!refField) {
140
- (0, error_1.throwError)(`Investigate: Relation field ${dmmfField.name} not found.`);
192
+ (0, error_1.throwError)(`${fieldName}: Relation field ${highlight(dmmfField.name)} not found.`);
141
193
  }
142
194
  return Object.assign(Object.assign({ kind: 'relation' }, shared), { relatedModelBacklinkFieldName: Types.toFieldName(refField.name), typeName: Types.toTypeName(dmmfField.type), unbrandedTypeName: getTsTypeForId(dmmfField), relationToModel: refModel });
143
195
  }
@@ -148,7 +200,7 @@ function parseModel({ dmmfModel, enums, models, config, }) {
148
200
  if (dmmfField.kind === 'scalar') {
149
201
  let validation = undefined;
150
202
  if (dmmfField.isList) {
151
- (0, error_1.throwError)(`Array fields with scalars (e.g. String[]) aren't supported! Use a relation instead. Field: ${dmmfModel.name}.${dmmfField.name}`);
203
+ (0, error_1.throwError)(`${fieldName}: Array fields with scalars (e.g. String[]) aren't supported! Use a relation instead. `);
152
204
  }
153
205
  if (dmmfField.type === 'Int') {
154
206
  validation = { type: 'int' };
@@ -161,11 +213,11 @@ function parseModel({ dmmfModel, enums, models, config, }) {
161
213
  if (dmmfField.kind === 'enum') {
162
214
  const fieldEnumDef = enums.find((e) => e.sourceName === dmmfField.type);
163
215
  if (!fieldEnumDef) {
164
- (0, error_1.throwError)(`Investigate: Field references unknown enum ${dmmfField.type}.`);
216
+ (0, error_1.throwError)(`${fieldName}: Field references unknown enum ${highlight(dmmfField.type)}.`);
165
217
  }
166
218
  return Object.assign(Object.assign({ kind: 'enum' }, shared), { typeName: getTsTypeForEnum(dmmfField), enumerator: fieldEnumDef });
167
219
  }
168
- (0, error_1.throwError)(`Investigate: Field ${shared.sourceName}.${shared.sourceName} is not scalar, enum nor relation.`);
220
+ (0, error_1.throwError)(`${fieldName} is not scalar, enum nor relation.`);
169
221
  })
170
222
  .filter((field) => !isFieldIgnored({ field }));
171
223
  const { idField, defaultField, nameField, createdAtField, updatedAtField } = validateFields({ fields, model: core });
@@ -188,6 +240,7 @@ function validateFields({ fields, model: { name } }) {
188
240
  let nameField = undefined;
189
241
  let defaultField = undefined;
190
242
  for (const field of fields) {
243
+ const fieldName = highlight(`${name}.${field.name}`);
191
244
  switch (field.kind) {
192
245
  case 'scalar':
193
246
  if (field.name === 'name') {
@@ -195,20 +248,20 @@ function validateFields({ fields, model: { name } }) {
195
248
  }
196
249
  if (field.attributes.isCreatedAt) {
197
250
  if (createdAtField) {
198
- throw new Error(`❌❌❌ Model ${name} has multiple createdAt fields`);
251
+ (0, error_1.throwError)(`${fieldName} has multiple createdAt fields`);
199
252
  }
200
253
  createdAtField = field;
201
254
  }
202
255
  if (field.attributes.isUpdatedAt) {
203
256
  if (updatedAtField) {
204
- throw new Error(`❌❌❌ Model ${name} has multiple updatedAt fields`);
257
+ (0, error_1.throwError)(`${fieldName} has multiple updatedAt fields`);
205
258
  }
206
259
  updatedAtField = field;
207
260
  }
208
261
  break;
209
262
  case 'id':
210
263
  if (idField) {
211
- throw new Error(`❌❌❌ Model ${name} has multiple id fields`);
264
+ (0, error_1.throwError)(`${fieldName} has multiple id fields`);
212
265
  }
213
266
  idField = field;
214
267
  break;
@@ -220,20 +273,20 @@ function validateFields({ fields, model: { name } }) {
220
273
  //handle default case
221
274
  if (field.attributes.isDefaultField && field.kind === 'scalar') {
222
275
  if (defaultField !== undefined) {
223
- throw new Error(`❌❌❌ Model ${name} has multiple default fields`);
276
+ (0, error_1.throwError)(`${fieldName} has multiple default fields`);
224
277
  }
225
278
  defaultField = field;
226
279
  }
227
280
  //handle name field
228
281
  if (field.attributes.isLabel) {
229
282
  if (labelField !== undefined) {
230
- throw new Error(`❌❌❌ Model ${name} has multiple name fields`);
283
+ (0, error_1.throwError)(`${fieldName} has multiple name fields`);
231
284
  }
232
285
  labelField = field;
233
286
  }
234
287
  }
235
288
  if (!idField) {
236
- throw new Error(`❌❌❌ Model ${name} does not have an id field`);
289
+ (0, error_1.throwError)(`Model ${highlight(name)} does not have an id field`);
237
290
  }
238
291
  return { idField, defaultField, nameField: (_a = labelField !== null && labelField !== void 0 ? labelField : nameField) !== null && _a !== void 0 ? _a : idField, createdAtField, updatedAtField };
239
292
  }
@@ -309,12 +362,12 @@ function getTsTypeForScalar(field) {
309
362
  return Types.toTypeName('number');
310
363
  case 'Json':
311
364
  case 'Bytes':
312
- (0, error_1.throwError)('Not implemented yet');
365
+ (0, error_1.throwError)(`Field ${highlight(field.name)}: Type ${field.type} Not implemented yet`);
313
366
  // While TypeScript understands that throwError never returns, eslint doesn't and complains.
314
367
  // Hence we ignore the fallthrough error.
315
368
  // eslint-disable-next-line no-fallthrough
316
369
  default:
317
- (0, error_1.throwError)(`Investigate: 'default' case in getTsTypeForScalar for field ${field.name} of type ${field.type}`);
370
+ (0, error_1.throwError)(`Investigate: 'default' case in getTsTypeForScalar for field ${highlight(field.name)} of type ${field.type}`);
318
371
  }
319
372
  }
320
373
  /**
@@ -332,7 +385,7 @@ function getTsTypeForId(field) {
332
385
  case 'Int':
333
386
  return Types.toTypeName('number');
334
387
  default:
335
- (0, error_1.throwError)(`The id field ${field.name} is of type ${field.type} - but only BigInt, Boolean, Decimal, Float, Int and String are supported for Ids.`);
388
+ (0, error_1.throwError)(`The id field ${highlight(field.name)} is of type ${field.type} - but only BigInt, Boolean, Decimal, Float, Int and String are supported for Ids.`);
336
389
  }
337
390
  }
338
391
  /**
@@ -347,9 +400,17 @@ function getTsTypeForEnum(field) {
347
400
  case 'Decimal':
348
401
  case 'Float':
349
402
  case 'Int':
350
- (0, error_1.throwError)(`The enum field ${field.name} is of type ${field.type} - but only String fields are supported for enums so far.`);
403
+ (0, error_1.throwError)(`The enum field ${highlight(field.name)} is of type ${field.type} - but only String fields are supported for enums so far.`);
351
404
  break;
352
405
  default:
353
406
  return Types.toTypeName(field.type);
354
407
  }
355
408
  }
409
+ /**
410
+ * Highlights a string cyan
411
+ *
412
+ * NOTE: We would normally use `chalk.cyan` here, but this causes an error in the generator, so we use this workaround.
413
+ */
414
+ function highlight(str) {
415
+ return `\u001B[36m${str}\u001B[39m`;
416
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postxl/generator",
3
- "version": "0.45.0",
3
+ "version": "0.47.0",
4
4
  "main": "./dist/generator.js",
5
5
  "typings": "./dist/generator.d.ts",
6
6
  "bin": {