@postxl/generator 0.13.0 → 0.15.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.
Files changed (42) hide show
  1. package/dist/generator.js +20 -0
  2. package/dist/generators/indices/businesslogicindex.generator.d.ts +9 -0
  3. package/dist/generators/indices/businesslogicindex.generator.js +19 -0
  4. package/dist/generators/indices/businesslogicmodule.generator.d.ts +9 -0
  5. package/dist/generators/indices/businesslogicmodule.generator.js +43 -0
  6. package/dist/generators/indices/businesslogicservice.generator.d.ts +9 -0
  7. package/dist/generators/indices/businesslogicservice.generator.js +32 -0
  8. package/dist/generators/indices/datamockmodule.generator.js +2 -2
  9. package/dist/generators/indices/datamodule.generator.js +22 -21
  10. package/dist/generators/indices/dataservice.generator.js +1 -1
  11. package/dist/generators/indices/seed-template-decoder.generator.d.ts +9 -0
  12. package/dist/generators/indices/seed-template-decoder.generator.js +137 -0
  13. package/dist/generators/indices/seed-template.generator.d.ts +9 -0
  14. package/dist/generators/indices/seed-template.generator.js +80 -0
  15. package/dist/generators/models/businesslogic.generator.d.ts +9 -0
  16. package/dist/generators/models/businesslogic.generator.js +172 -0
  17. package/dist/generators/models/react.generator/context.generator.js +5 -3
  18. package/dist/generators/models/react.generator/library.generator.js +6 -3
  19. package/dist/generators/models/react.generator/lookup.generator.js +1 -1
  20. package/dist/generators/models/react.generator/modals.generator.d.ts +1 -1
  21. package/dist/generators/models/react.generator/modals.generator.js +128 -65
  22. package/dist/generators/models/repository.generator.js +38 -15
  23. package/dist/generators/models/route.generator.js +6 -6
  24. package/dist/generators/models/seed.generator.js +49 -25
  25. package/dist/generators/models/stub.generator.js +21 -14
  26. package/dist/generators/models/types.generator.js +34 -3
  27. package/dist/lib/attributes.d.ts +1 -1
  28. package/dist/lib/imports.d.ts +1 -1
  29. package/dist/lib/imports.js +12 -3
  30. package/dist/lib/meta.d.ts +150 -14
  31. package/dist/lib/meta.js +34 -1
  32. package/dist/lib/schema/fields.d.ts +5 -1
  33. package/dist/lib/schema/schema.d.ts +26 -1
  34. package/dist/lib/schema/schema.js +1 -1
  35. package/dist/lib/schema/types.d.ts +2 -2
  36. package/dist/lib/utils/file.js +2 -0
  37. package/dist/lib/utils/logger.d.ts +5 -5
  38. package/dist/lib/utils/string.d.ts +5 -0
  39. package/dist/lib/utils/string.js +10 -2
  40. package/dist/prisma/attributes.js +20 -9
  41. package/dist/prisma/parse.js +17 -3
  42. package/package.json +3 -2
@@ -41,14 +41,16 @@ function generateRepository({ model, meta }) {
41
41
  };
42
42
  };
43
43
  const indexes = model.attributes.index ? [getIndexDefinition(model.attributes.index)] : [];
44
- const defaultValueInitFn = `
44
+ const defaultValueInitFn = model.defaultField
45
+ ? `
45
46
  if (item.${(_a = model.defaultField) === null || _a === void 0 ? void 0 : _a.name}) {
46
47
  if (this.defaultValue) {
47
48
  console.warn(\`More than one default ${meta.userFriendlyName} found! \${this.defaultValue.id} and \${item.id}\`)
48
49
  }
49
50
  this.defaultValue = item
50
51
  }
51
- `;
52
+ `
53
+ : '';
52
54
  const defaultValueInitCheckFn = `
53
55
  if (!this.db.isCLI && !this.defaultValue) {
54
56
  throw new Error('No default ${meta.userFriendlyName} found!')
@@ -58,14 +60,16 @@ function generateRepository({ model, meta }) {
58
60
  const idIntInitFn = `this.currentMaxId = (await this.db.${meta.data.repository.getMethodFnName}.aggregate({ _max: { ${idField.sourceName}: true } }))._max.${idField.sourceName} ?? 0`;
59
61
  const imports = imports_1.ImportsGenerator.from(meta.data.repoFilePath)
60
62
  .addImport({
61
- items: [model.typeName, model.brandedIdType, meta.types.toBrandedIdTypeFnName],
63
+ items: [model.typeName, model.brandedIdType, ...(isGenerated ? [meta.types.toBrandedIdTypeFnName] : [])],
62
64
  from: meta.types.importPath,
63
65
  })
64
- .addImport({ items: [meta.types.zodDecoderFnName], from: meta.types.importPath })
65
66
  .addImport({
66
67
  items: [(0, types_1.toClassName)('Repository')],
67
68
  from: (0, types_1.toFileName)(model.schemaConfig.paths.dataLibPath + 'repository.type'),
68
69
  });
70
+ if (!model.attributes.inMemoryOnly) {
71
+ imports.addImport({ items: [meta.types.zodDecoderFnName], from: meta.types.importPath });
72
+ }
69
73
  relations.forEach((r) => {
70
74
  imports.addImport({
71
75
  items: [r.meta.types.brandedIdType],
@@ -74,8 +78,12 @@ function generateRepository({ model, meta }) {
74
78
  });
75
79
  return `
76
80
  import { Injectable, Logger } from '@nestjs/common'
81
+ ${model.attributes.inMemoryOnly
82
+ ? ''
83
+ : `
77
84
  import { DbService, ${model.sourceName} as DbType } from '@${model.schemaConfig.project}/db'
78
85
  import { format, pluralize ${indexes.length === 0 ? '' : ', NestedMap'} } from '@pxl/common'
86
+ `}
79
87
 
80
88
  ${imports.generate()}
81
89
 
@@ -235,6 +243,11 @@ export class ${meta.data.repositoryClassName} implements Repository<
235
243
  ${[...model.fields.values()].map((f) => `${f.sourceName}: item.${f.name}`).join(',\n')}
236
244
  }
237
245
  }
246
+ ${model.attributes.inMemoryOnly
247
+ ? `
248
+ // Non-mocked version is async - so we keep type-compatible signatures for create() and createWithId()
249
+ // eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars`
250
+ : ''}
238
251
  public async createWithId(item: Omit<${model.typeName}, '${idField.name}'> & {
239
252
  id: ${model.brandedIdType}
240
253
  }): Promise<${model.typeName}> {
@@ -254,6 +267,11 @@ export class ${meta.data.repositoryClassName} implements Repository<
254
267
  return newItem
255
268
  }
256
269
 
270
+ ${model.attributes.inMemoryOnly
271
+ ? `
272
+ // Non-mocked version is async - so we keep type-compatible signatures for create() and createWithId()
273
+ // eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-unused-vars`
274
+ : ''}
257
275
  public async create(
258
276
  item: Omit<${model.typeName}, 'id'>
259
277
  ): Promise<${model.typeName}> {
@@ -405,17 +423,22 @@ export class ${meta.data.repositoryClassName} implements Repository<
405
423
  }
406
424
  `)
407
425
  .join('\n')}
408
- ${maxLengthStringFields.map((f) => {
409
- return `
410
- /**
411
- * Utility function that ensures that the ${f.name} field has a max length of ${f.attributes.maxLength}
412
- */
413
- private ${getEnsureMaxLengthFnName(f)}(item: { ${f.name}?: string }) {
414
- if (!item.${f.name}) return
415
- if (item.${f.name}.length <= ${f.attributes.maxLength}) return
416
- item.${f.name} = item.${f.name}.substring(0, ${f.attributes.maxLength - 4}) + \`...\`
417
- }`;
418
- })}
426
+
427
+ ${maxLengthStringFields
428
+ .map((f) => `
429
+ /**
430
+ * Utility function that ensures that the ${f.name} field has a max length of ${f.attributes.maxLength}
431
+ */
432
+ private ${getEnsureMaxLengthFnName(f)}(item: { ${f.name}?: string }) {
433
+ if (!item.${f.name}) {
434
+ return
435
+ }
436
+ if (item.${f.name}.length <= ${f.attributes.maxLength}) {
437
+ return
438
+ }
439
+ item.${f.name} = item.${f.name}.substring(0, ${f.attributes.maxLength - 4}) + \`...\`
440
+ }`)
441
+ .join('\n')}
419
442
 
420
443
  ${uniqueStringFields
421
444
  .map((f) => {
@@ -11,7 +11,7 @@ const imports_1 = require("../../lib/imports");
11
11
  function generateRoute({ model, meta }) {
12
12
  const { idField, defaultField } = model;
13
13
  const defaultValueMethod = `
14
- getDefault: procedure.query(({ ctx }) => ctx.dataService.${meta.data.dataServiceName}.defaultValue),
14
+ getDefault: procedure.query(({ ctx }) => ctx.data.${meta.data.dataServiceName}.defaultValue),
15
15
  `;
16
16
  const createMethod = getCreateMethod({ model, meta });
17
17
  const updateMethod = getUpdateMethod({ model, meta });
@@ -38,8 +38,8 @@ export const ${meta.trpc.routerName} = router({
38
38
 
39
39
  get: procedure
40
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()),
41
+ .query(({ input, ctx }) => ctx.data.${meta.data.dataServiceName}.get(input)),
42
+ getMap: procedure.query(({ ctx }) => ctx.data.${meta.data.dataServiceName}.getAll()),
43
43
 
44
44
  ${omit(createMethod, model.attributes.skipCreate)}
45
45
  ${omit(updateMethod, model.attributes.skipUpdate)}
@@ -66,7 +66,7 @@ function getCreateMethod({ model: { fields }, meta }) {
66
66
  create: procedure.input(z.object({
67
67
  ${parameters}
68
68
  }))
69
- .mutation(({ input, ctx }) => ctx.dataService.${meta.data.dataServiceName}.create(input)),
69
+ .mutation(({ input, ctx }) => ctx.data.${meta.data.dataServiceName}.create(input)),
70
70
  `;
71
71
  }
72
72
  function getUpdateMethod({ model: { fields }, meta }) {
@@ -79,14 +79,14 @@ function getUpdateMethod({ model: { fields }, meta }) {
79
79
  ${parameters}
80
80
  })
81
81
  )
82
- .mutation(({ input, ctx }) => ctx.dataService.${meta.data.dataServiceName}.update(input)),
82
+ .mutation(({ input, ctx }) => ctx.data.${meta.data.dataServiceName}.update(input)),
83
83
  `;
84
84
  }
85
85
  function getDeleteMethod({ idField, meta }) {
86
86
  return `
87
87
  delete: procedure
88
88
  .input(z.${idField.unbrandedTypeName}().transform(${meta.types.toBrandedIdTypeFnName}))
89
- .mutation(({ input, ctx }) => ctx.dataService.${meta.data.dataServiceName}.delete(input)),
89
+ .mutation(({ input, ctx }) => ctx.data.${meta.data.dataServiceName}.delete(input)),
90
90
  `;
91
91
  }
92
92
  /**
@@ -95,39 +95,60 @@ function generateFieldData({ field, model, index, exampleMode, }) {
95
95
  throw new types_1.ExhaustiveSwitchCheck(field);
96
96
  }
97
97
  }
98
- function generateFieldDataId({ field, model, index }) {
98
+ function generateFieldDataId({ field, index }) {
99
99
  const idModelMeta = (0, meta_1.getModelMetadata)({ model: field.model });
100
100
  return `${idModelMeta.types.toBrandedIdTypeFnName}(${field.unbrandedTypeName === 'string' ? `'${index}'` : index})`;
101
101
  }
102
102
  function quoteSingleQuote(str) {
103
- return str !== null ? str.replace(/'/g, "\\'") : null;
103
+ return str.replace(/'/g, "\\'");
104
104
  }
105
105
  function generateFieldDataScalar({ field, model, index, exampleMode, }) {
106
106
  const { hasExample, example } = getFieldExample({ field, model, index, exampleMode });
107
107
  switch (field.typeName) {
108
- case 'string':
109
- const result = hasExample ? example : generateFieldDataString({ field, model, index });
110
- if (result === null)
108
+ case 'string': {
109
+ if (hasExample && typeof example === 'string') {
110
+ return `'${quoteSingleQuote(example)}'`;
111
+ }
112
+ const result = generateFieldDataString({ field, model, index });
113
+ if (result === null) {
111
114
  return 'null';
112
- if (result === undefined)
115
+ }
116
+ if (result === undefined) {
113
117
  return 'undefined';
114
- return `'${quoteSingleQuote(result)}'`;
115
- case 'number':
116
- return hasExample ? example : generateFieldDataNumber({ field, model, index });
117
- case 'boolean':
118
- return hasExample ? example : generateFieldDataBoolean({ field, model, index });
119
- case 'Date':
120
- return hasExample ? example : generateFieldDataDate({ field, model, index });
121
- default:
118
+ }
119
+ return `'${result.replace(/'/g, "\\'")}'`;
120
+ }
121
+ case 'number': {
122
+ if (hasExample) {
123
+ return `${example}`;
124
+ }
125
+ return generateFieldDataNumber();
126
+ }
127
+ case 'boolean': {
128
+ if (hasExample) {
129
+ return `${example}`;
130
+ }
131
+ return generateFieldDataBoolean();
132
+ }
133
+ case 'Date': {
134
+ if (hasExample) {
135
+ // TODO: Maybe we should parse the date example to correct format?
136
+ return `${example}`;
137
+ }
138
+ return generateFieldDataDate();
139
+ }
140
+ default: {
122
141
  console.warn(`Unknown scalar type: ${field.typeName}`);
123
142
  return '';
143
+ }
124
144
  }
125
145
  }
126
- function getFieldExample({ field, model, index, exampleMode, }) {
127
- if (exampleMode.mode === 'NoExamples')
128
- return { hasExample: false, example: undefined };
146
+ function getFieldExample({ field, index, exampleMode, }) {
147
+ if (exampleMode.mode === 'NoExamples') {
148
+ return { hasExample: false };
149
+ }
129
150
  if (!field.attributes.examples || field.attributes.examples.length === 0) {
130
- return { hasExample: false, example: undefined };
151
+ return { hasExample: false };
131
152
  }
132
153
  switch (exampleMode.mode) {
133
154
  case 'Permutations': {
@@ -138,30 +159,33 @@ function getFieldExample({ field, model, index, exampleMode, }) {
138
159
  const example = field.attributes.examples[(index - 1) % field.attributes.examples.length];
139
160
  return { hasExample: true, example };
140
161
  }
141
- default:
162
+ default: {
142
163
  throw new types_1.ExhaustiveSwitchCheck(exampleMode);
164
+ }
143
165
  }
144
166
  }
145
167
  function generateFieldDataString({ field, model, index }) {
146
- if (field.name === 'name')
168
+ if (field.name === 'name') {
147
169
  return `${(0, string_1.toPascalCase)(model.name)} ${index}`;
148
- if (field.name === 'email')
170
+ }
171
+ if (field.name === 'email') {
149
172
  return faker_1.faker.internet.email();
173
+ }
150
174
  return faker_1.faker.lorem.words(3);
151
175
  }
152
- function generateFieldDataNumber({}) {
176
+ function generateFieldDataNumber() {
153
177
  return faker_1.faker.datatype.float({ precision: 0.1, min: 0, max: 1 }).toString();
154
178
  }
155
- function generateFieldDataBoolean({}) {
179
+ function generateFieldDataBoolean() {
156
180
  return faker_1.faker.datatype.boolean().toString();
157
181
  }
158
- function generateFieldDataDate({ field, model, index }) {
182
+ function generateFieldDataDate() {
159
183
  const d = faker_1.faker.date.past(3, '2023-04-01T00:00:00.000Z');
160
184
  //set time to midnight UTC
161
185
  d.setUTCHours(0, 0, 0, 0);
162
186
  return `new Date('${d.toISOString()}')`;
163
187
  }
164
- function generateFieldDataRelation({ field, model, index, itemCount, }) {
188
+ function generateFieldDataRelation({ field, itemCount, }) {
165
189
  const referenceId = faker_1.faker.datatype.number({ min: 1, max: itemCount });
166
190
  const refModelMeta = (0, meta_1.getModelMetadata)({ model: field.relationToModel });
167
191
  const brandingFn = refModelMeta.types.toBrandedIdTypeFnName;
@@ -14,13 +14,7 @@ function generateStub({ model, meta }) {
14
14
  items: [model.typeName, meta.types.toBrandedIdTypeFnName],
15
15
  from: meta.types.importPath,
16
16
  });
17
- for (const relation of (0, fields_1.getRelationFields)(model)) {
18
- const depMeta = (0, meta_1.getModelMetadata)({ model: relation.relationToModel });
19
- imports.addImport({
20
- items: [relation.relationToModel.typeName, depMeta.types.toBrandedIdTypeFnName],
21
- from: meta.types.importPath,
22
- });
23
- }
17
+ const assignments = getAssignmentStatementModel({ fields, imports });
24
18
  return `
25
19
  ${imports.generate()}
26
20
 
@@ -28,7 +22,7 @@ ${imports.generate()}
28
22
  * Utility object containing default values for all fields in a model.
29
23
  */
30
24
  export const ${meta.data.defaultStubConstantName}: ${model.typeName} = {
31
- ${getAssigmentStatementModel({ fields })}
25
+ ${assignments}
32
26
  }
33
27
 
34
28
  /**
@@ -46,25 +40,38 @@ exports.generateStub = generateStub;
46
40
  /**
47
41
  * Return an assignment statement for a model where each field is assigned null or its type's default value.
48
42
  */
49
- function getAssigmentStatementModel({ fields }) {
43
+ function getAssignmentStatementModel({ fields, imports }) {
50
44
  return fields
51
45
  .map((f) => {
52
46
  if (!f.isRequired) {
53
47
  return `${f.name}: null`;
54
48
  }
55
49
  switch (f.kind) {
56
- case 'scalar':
50
+ case 'scalar': {
57
51
  return `${f.name}: ${(0, fields_1.getDefaultValueForType)(f.typeName)}`;
58
- case 'id':
52
+ }
53
+ case 'id': {
59
54
  const idRefMeta = (0, meta_1.getModelMetadata)({ model: f.model });
55
+ imports.addImport({
56
+ items: [idRefMeta.types.toBrandedIdTypeFnName],
57
+ from: idRefMeta.types.importPath,
58
+ });
60
59
  return `${f.name}: ${idRefMeta.types.toBrandedIdTypeFnName}(${(0, fields_1.getDefaultValueForType)(f.unbrandedTypeName)})`;
61
- case 'relation':
60
+ }
61
+ case 'relation': {
62
62
  const refModelMeta = (0, meta_1.getModelMetadata)({ model: f.relationToModel });
63
+ imports.addImport({
64
+ items: [refModelMeta.types.toBrandedIdTypeFnName],
65
+ from: refModelMeta.types.importPath,
66
+ });
63
67
  return `${f.name}: ${refModelMeta.types.toBrandedIdTypeFnName}(${(0, fields_1.getDefaultValueForType)(f.unbrandedTypeName)})`;
64
- case 'enum':
68
+ }
69
+ case 'enum': {
65
70
  return `${f.name}: "${f.enumerator.values[0]}"`;
66
- default:
71
+ }
72
+ default: {
67
73
  throw new types_1.ExhaustiveSwitchCheck(f);
74
+ }
68
75
  }
69
76
  })
70
77
  .join(',\n');
@@ -12,6 +12,7 @@ const types_1 = require("../../lib/types");
12
12
  function generateModelTypes({ model, meta }) {
13
13
  const idField = model.idField;
14
14
  const imports = imports_1.ImportsGenerator.from(meta.types.filePath);
15
+ let hasLinkedItems = false;
15
16
  for (const relation of (0, fields_1.getRelationFields)(model)) {
16
17
  if (relation.relationToModel.typeName === model.typeName) {
17
18
  continue;
@@ -19,9 +20,10 @@ function generateModelTypes({ model, meta }) {
19
20
  const refModel = relation.relationToModel;
20
21
  const refMeta = (0, meta_1.getModelMetadata)({ model: refModel });
21
22
  imports.addImport({
22
- items: [refMeta.types.toBrandedIdTypeFnName, refModel.brandedIdType],
23
+ items: [refMeta.types.toBrandedIdTypeFnName, refModel.brandedIdType, refMeta.types.typeName],
23
24
  from: refMeta.types.filePath,
24
25
  });
26
+ hasLinkedItems = true;
25
27
  }
26
28
  for (const f of (0, fields_1.getEnumFields)(model)) {
27
29
  const refEnumMeta = (0, meta_1.getEnumMetadata)({ enumerator: f.enumerator });
@@ -42,7 +44,7 @@ ${model.description
42
44
  * ${model.description.split('\n').join('\n * ')}
43
45
  */`
44
46
  : ''}
45
- export type ${model.typeName} = {
47
+ export type ${meta.types.typeName} = {
46
48
  ${model.fields
47
49
  .map((f) => `
48
50
  ${getFieldComment(f)}
@@ -50,6 +52,17 @@ ${f.name}: ${getFieldType(f)}${f.isRequired ? '' : ' | null'}`)
50
52
  .join('\n')}
51
53
  }
52
54
 
55
+ ${!hasLinkedItems
56
+ ? ``
57
+ : `
58
+ export type ${meta.types.linkedTypeName} = {
59
+ ${model.fields
60
+ .map((f) => `
61
+ ${getFieldComment(f)}
62
+ ${getLinkedFieldType(f)}${f.isRequired ? '' : ' | null'}`)
63
+ .join('\n')}
64
+ }`}
65
+
53
66
  /**
54
67
  * Branded Id type that should be used to identify an instance of this model.
55
68
  */
@@ -90,8 +103,9 @@ function getFieldComment(f) {
90
103
  ${comment}*/`;
91
104
  }
92
105
  function getFieldExamples(f) {
93
- if (!f.attributes.examples)
106
+ if (!f.attributes.examples) {
94
107
  return undefined;
108
+ }
95
109
  return `Examples: ${f.attributes.examples.map((e) => `"${e}"`).join(', ')}`;
96
110
  }
97
111
  /**
@@ -111,3 +125,20 @@ function getFieldType(f) {
111
125
  throw new types_1.ExhaustiveSwitchCheck(f);
112
126
  }
113
127
  }
128
+ /**
129
+ * Converts a field to a TypeScript type definition with linked fields.
130
+ */
131
+ function getLinkedFieldType(f) {
132
+ switch (f.kind) {
133
+ case 'enum':
134
+ return `${f.name}: ${f.typeName}`;
135
+ case 'relation':
136
+ return `${f.relatedModelBacklinkFieldName}: ${f.relationToModel.typeName}`;
137
+ case 'id':
138
+ return `${f.name}: ${f.model.brandedIdType}`;
139
+ case 'scalar':
140
+ return `${f.name}: ${f.typeName}`;
141
+ default:
142
+ throw new types_1.ExhaustiveSwitchCheck(f);
143
+ }
144
+ }
@@ -51,7 +51,7 @@ export type FieldAttributes = {
51
51
  /**
52
52
  * Schema tag: ´@@Examples("Example1", "Example2")`
53
53
  */
54
- examples?: unknown[];
54
+ examples?: (string | number | boolean)[];
55
55
  /**
56
56
  * Schema tag: ´@@DefaultField()`
57
57
  * The property of the model that identifies the default row.
@@ -25,7 +25,7 @@ export declare class ImportsGenerator {
25
25
  * NOTE: You should never add no items from a given path.
26
26
  */
27
27
  addImport({ items, from, }: {
28
- items: (Types.Function | Types.ClassName | Types.TypeName | Types.VariableName)[];
28
+ items: (Types.Fnction | Types.ClassName | Types.TypeName | Types.VariableName)[];
29
29
  from: Types.Path;
30
30
  }): ImportsGenerator;
31
31
  /**
@@ -41,15 +41,24 @@ class ImportsGenerator {
41
41
  * Returns the TypeScript import statement.
42
42
  */
43
43
  generate() {
44
- const stetements = Object.entries(this._imports)
45
- .sort(([a], [b]) => a.localeCompare(b))
44
+ const statements = Object.entries(this._imports)
45
+ .sort(([a], [b]) => {
46
+ // //in case a or b start with "@", they will be sorted first
47
+ // if (a.startsWith('@') && !b.startsWith('@')) {
48
+ // return -1
49
+ // }
50
+ // if (!a.startsWith('@') && b.startsWith('@')) {
51
+ // return 1
52
+ // }
53
+ return a.localeCompare(b);
54
+ })
46
55
  .map(([path, items]) => {
47
56
  const alphabeticallySortedItems = Array.from(items).sort((a, b) => a.localeCompare(b));
48
57
  // NOTE: You cannot remove imports and we check that there's at least one imported item for every path.
49
58
  return `import { ${alphabeticallySortedItems.join(', ')} } from '${path}'`;
50
59
  })
51
60
  .join('\n');
52
- return stetements;
61
+ return statements;
53
62
  }
54
63
  }
55
64
  exports.ImportsGenerator = ImportsGenerator;