@strapi/typescript-utils 4.3.0-beta.2 → 4.3.2-alpha.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/lib/__tests__/generators/schemas/attributes.test.js +645 -23
- package/lib/__tests__/generators/schemas/global.test.js +108 -0
- package/lib/generators/schemas/attributes.js +4 -8
- package/lib/generators/schemas/index.js +53 -45
- package/lib/generators/schemas/utils.js +5 -0
- package/package.json +2 -2
|
@@ -6,21 +6,97 @@ const consoleWarnMock = jest.spyOn(console, 'warn').mockImplementation();
|
|
|
6
6
|
|
|
7
7
|
const ts = require('typescript');
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const attributeToPropertySignature = require('../../../generators/schemas/attributes');
|
|
10
|
+
const {
|
|
11
|
+
getAttributeType,
|
|
12
|
+
getAttributeModifiers,
|
|
13
|
+
} = require('../../../generators/schemas/attributes');
|
|
10
14
|
const { addImport } = require('../../../generators/schemas/imports');
|
|
11
15
|
|
|
16
|
+
// TODO: emit definition (to a string) & also check snapshots based on that. It would allow checking both the structure & the output.
|
|
12
17
|
describe('Attributes', () => {
|
|
13
18
|
afterEach(() => {
|
|
14
19
|
jest.resetAllMocks();
|
|
15
20
|
});
|
|
16
21
|
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
describe('Attribute to Property Signature', () => {
|
|
23
|
+
const schema = { uid: 'api::foo.foo' };
|
|
24
|
+
const attributeName = 'foo';
|
|
19
25
|
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
const toPropertySignature = attribute => {
|
|
27
|
+
return attributeToPropertySignature(schema, attributeName, attribute);
|
|
28
|
+
};
|
|
22
29
|
|
|
23
|
-
|
|
30
|
+
const defaultAssertion = node => {
|
|
31
|
+
expect(node.kind).toBe(ts.SyntaxKind.PropertySignature);
|
|
32
|
+
expect(node.name.escapedText).toBe(attributeName);
|
|
33
|
+
expect(node.type.kind).toBe(ts.SyntaxKind.IntersectionType);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
test('Invalid attribute type', () => {
|
|
37
|
+
const attribute = { type: 'invalid' };
|
|
38
|
+
const prop = toPropertySignature(attribute);
|
|
39
|
+
|
|
40
|
+
expect(prop).toBeNull();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('Attribute without type argument', () => {
|
|
44
|
+
const attribute = { type: 'string' };
|
|
45
|
+
const prop = toPropertySignature(attribute);
|
|
46
|
+
|
|
47
|
+
defaultAssertion(prop);
|
|
48
|
+
|
|
49
|
+
expect(prop.type.types).toHaveLength(1);
|
|
50
|
+
expect(prop.type.types[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
51
|
+
expect(prop.type.types[0].typeName.escapedText).toBe('StringAttribute');
|
|
52
|
+
expect(prop.type.types[0].typeArguments).toBeUndefined();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('Attribute with type argument', () => {
|
|
56
|
+
const attribute = { type: 'component', component: 'default.comp' };
|
|
57
|
+
const prop = toPropertySignature(attribute);
|
|
58
|
+
|
|
59
|
+
defaultAssertion(prop);
|
|
60
|
+
|
|
61
|
+
expect(prop.type.types).toHaveLength(1);
|
|
62
|
+
expect(prop.type.types[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
63
|
+
expect(prop.type.types[0].typeName.escapedText).toBe('ComponentAttribute');
|
|
64
|
+
expect(prop.type.types[0].typeArguments).toHaveLength(1);
|
|
65
|
+
expect(prop.type.types[0].typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
66
|
+
expect(prop.type.types[0].typeArguments[0].text).toBe('default.comp');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('Attribute with type argument and options', () => {
|
|
70
|
+
const attribute = {
|
|
71
|
+
type: 'enumeration',
|
|
72
|
+
enum: ['a', 'b'],
|
|
73
|
+
default: 'b',
|
|
74
|
+
configurable: false,
|
|
75
|
+
};
|
|
76
|
+
const prop = toPropertySignature(attribute);
|
|
77
|
+
|
|
78
|
+
defaultAssertion(prop);
|
|
79
|
+
|
|
80
|
+
expect(prop.type.types).toHaveLength(2);
|
|
81
|
+
|
|
82
|
+
const [attributeType, requiredOptionType] = prop.type.types;
|
|
83
|
+
|
|
84
|
+
expect(attributeType.kind).toBe(ts.SyntaxKind.TypeReference);
|
|
85
|
+
expect(attributeType.typeName.escapedText).toBe('EnumerationAttribute');
|
|
86
|
+
expect(attributeType.typeArguments).toHaveLength(1);
|
|
87
|
+
expect(attributeType.typeArguments[0].kind).toBe(ts.SyntaxKind.TupleType);
|
|
88
|
+
expect(attributeType.typeArguments[0].elements[0].text).toBe('a');
|
|
89
|
+
expect(attributeType.typeArguments[0].elements[1].text).toBe('b');
|
|
90
|
+
|
|
91
|
+
expect(requiredOptionType.kind).toBe(ts.SyntaxKind.TypeReference);
|
|
92
|
+
expect(requiredOptionType.typeName.escapedText).toBe('DefaultTo');
|
|
93
|
+
expect(requiredOptionType.typeArguments).toHaveLength(1);
|
|
94
|
+
expect(requiredOptionType.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
95
|
+
expect(requiredOptionType.typeArguments[0].text).toBe('b');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('Get Attribute Type / Mappers', () => {
|
|
24
100
|
test('If the attribute type is not valid then log an error and exit early without importing the type', () => {
|
|
25
101
|
const typeNode = getAttributeType('foo', { type: 'invalid', uid: 'api::foo.foo' });
|
|
26
102
|
|
|
@@ -31,17 +107,34 @@ describe('Attributes', () => {
|
|
|
31
107
|
expect(addImport).not.toHaveBeenCalled();
|
|
32
108
|
});
|
|
33
109
|
|
|
34
|
-
test(
|
|
35
|
-
|
|
110
|
+
test.each([
|
|
111
|
+
['string', 'StringAttribute'],
|
|
112
|
+
['text', 'TextAttribute'],
|
|
113
|
+
['richtext', 'RichTextAttribute'],
|
|
114
|
+
['password', 'PasswordAttribute'],
|
|
115
|
+
['email', 'EmailAttribute'],
|
|
116
|
+
['date', 'DateAttribute'],
|
|
117
|
+
['time', 'TimeAttribute'],
|
|
118
|
+
['datetime', 'DateTimeAttribute'],
|
|
119
|
+
['timestamp', 'TimestampAttribute'],
|
|
120
|
+
['integer', 'IntegerAttribute'],
|
|
121
|
+
['biginteger', 'BigIntegerAttribute'],
|
|
122
|
+
['float', 'FloatAttribute'],
|
|
123
|
+
['decimal', 'DecimalAttribute'],
|
|
124
|
+
['boolean', 'BooleanAttribute'],
|
|
125
|
+
['json', 'JSONAttribute'],
|
|
126
|
+
['media', 'MediaAttribute'],
|
|
127
|
+
])('Basic %p attribute should map to a %p type', (type, expectedType) => {
|
|
128
|
+
const typeNode = getAttributeType('foo', { type });
|
|
36
129
|
|
|
37
130
|
expect(ts.isTypeNode(typeNode)).toBeTruthy();
|
|
38
131
|
|
|
39
132
|
expect(typeNode.kind).toBe(ts.SyntaxKind.TypeReference);
|
|
40
|
-
expect(typeNode.typeName.escapedText).toBe(
|
|
133
|
+
expect(typeNode.typeName.escapedText).toBe(expectedType);
|
|
41
134
|
expect(typeNode.typeArguments).toBeUndefined();
|
|
42
135
|
|
|
43
136
|
expect(consoleWarnMock).not.toHaveBeenCalled();
|
|
44
|
-
expect(addImport).toHaveBeenCalledWith(
|
|
137
|
+
expect(addImport).toHaveBeenCalledWith(expectedType);
|
|
45
138
|
});
|
|
46
139
|
|
|
47
140
|
describe('Complex types (with generic type parameters)', () => {
|
|
@@ -55,27 +148,556 @@ describe('Attributes', () => {
|
|
|
55
148
|
expect(addImport).toHaveBeenCalledWith(typeName);
|
|
56
149
|
};
|
|
57
150
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
151
|
+
describe('Enumeration', () => {
|
|
152
|
+
test('Enumeration with an enum property', () => {
|
|
153
|
+
const attribute = { type: 'enumeration', enum: ['a', 'b', 'c'] };
|
|
154
|
+
const typeNode = getAttributeType('foo', attribute);
|
|
155
|
+
|
|
156
|
+
defaultAssertions(typeNode, 'EnumerationAttribute');
|
|
157
|
+
|
|
158
|
+
expect(typeNode.typeArguments).toHaveLength(1);
|
|
159
|
+
expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.TupleType);
|
|
160
|
+
|
|
161
|
+
const tupleElements = typeNode.typeArguments[0].elements;
|
|
162
|
+
|
|
163
|
+
attribute.enum.forEach((value, index) => {
|
|
164
|
+
const element = tupleElements[index];
|
|
165
|
+
|
|
166
|
+
expect(element.kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
167
|
+
expect(element.text).toBe(value);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('UID', () => {
|
|
173
|
+
test('UID with no options and no target field', () => {
|
|
174
|
+
const attribute = { type: 'uid' };
|
|
175
|
+
const typeNode = getAttributeType('foo', attribute);
|
|
176
|
+
|
|
177
|
+
defaultAssertions(typeNode, 'UIDAttribute');
|
|
178
|
+
|
|
179
|
+
expect(typeNode.typeArguments).toBeUndefined();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('UID with a target field and no options', () => {
|
|
183
|
+
const attribute = { type: 'uid', targetField: 'bar' };
|
|
184
|
+
const typeNode = getAttributeType('foo', attribute, 'api::bar.bar');
|
|
185
|
+
|
|
186
|
+
defaultAssertions(typeNode, 'UIDAttribute');
|
|
187
|
+
|
|
188
|
+
expect(typeNode.typeArguments).not.toBeUndefined();
|
|
189
|
+
expect(typeNode.typeArguments).toHaveLength(2);
|
|
190
|
+
|
|
191
|
+
expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
192
|
+
expect(typeNode.typeArguments[0].text).toBe('api::bar.bar');
|
|
193
|
+
|
|
194
|
+
expect(typeNode.typeArguments[1].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
195
|
+
expect(typeNode.typeArguments[1].text).toBe('bar');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test('UID with partial options and no target field', () => {
|
|
199
|
+
const attribute = { type: 'uid', options: { separator: '_' } };
|
|
200
|
+
const typeNode = getAttributeType('foo', attribute);
|
|
201
|
+
|
|
202
|
+
defaultAssertions(typeNode, 'UIDAttribute');
|
|
203
|
+
|
|
204
|
+
expect(typeNode.typeArguments).toHaveLength(3);
|
|
205
|
+
|
|
206
|
+
expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.UndefinedKeyword);
|
|
207
|
+
expect(typeNode.typeArguments[1].kind).toBe(ts.SyntaxKind.UndefinedKeyword);
|
|
208
|
+
|
|
209
|
+
const optionsLiteralNode = typeNode.typeArguments[2];
|
|
210
|
+
|
|
211
|
+
expect(optionsLiteralNode.kind).toBe(ts.SyntaxKind.TypeLiteral);
|
|
212
|
+
expect(optionsLiteralNode.members).toHaveLength(1);
|
|
213
|
+
|
|
214
|
+
expect(optionsLiteralNode.members[0].kind).toBe(ts.SyntaxKind.PropertyDeclaration);
|
|
215
|
+
|
|
216
|
+
expect(optionsLiteralNode.members[0].name.kind).toBe(ts.SyntaxKind.Identifier);
|
|
217
|
+
expect(optionsLiteralNode.members[0].name.escapedText).toBe('separator');
|
|
218
|
+
|
|
219
|
+
expect(optionsLiteralNode.members[0].type.kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
220
|
+
expect(optionsLiteralNode.members[0].type.text).toBe('_');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test('UID with options and a target field', () => {
|
|
224
|
+
const attribute = { type: 'uid', options: { separator: '_' }, targetField: 'bar' };
|
|
225
|
+
const typeNode = getAttributeType('foo', attribute, 'api::bar.bar');
|
|
61
226
|
|
|
62
|
-
|
|
227
|
+
defaultAssertions(typeNode, 'UIDAttribute');
|
|
63
228
|
|
|
64
|
-
|
|
65
|
-
expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.TupleType);
|
|
229
|
+
expect(typeNode.typeArguments).toHaveLength(3);
|
|
66
230
|
|
|
67
|
-
|
|
231
|
+
expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
232
|
+
expect(typeNode.typeArguments[0].text).toBe('api::bar.bar');
|
|
68
233
|
|
|
69
|
-
|
|
70
|
-
|
|
234
|
+
expect(typeNode.typeArguments[1].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
235
|
+
expect(typeNode.typeArguments[1].text).toBe('bar');
|
|
71
236
|
|
|
72
|
-
|
|
73
|
-
|
|
237
|
+
const optionsLiteralNode = typeNode.typeArguments[2];
|
|
238
|
+
|
|
239
|
+
expect(optionsLiteralNode.kind).toBe(ts.SyntaxKind.TypeLiteral);
|
|
240
|
+
expect(optionsLiteralNode.members).toHaveLength(1);
|
|
241
|
+
|
|
242
|
+
expect(optionsLiteralNode.members[0].kind).toBe(ts.SyntaxKind.PropertyDeclaration);
|
|
243
|
+
|
|
244
|
+
expect(optionsLiteralNode.members[0].name.kind).toBe(ts.SyntaxKind.Identifier);
|
|
245
|
+
expect(optionsLiteralNode.members[0].name.escapedText).toBe('separator');
|
|
246
|
+
|
|
247
|
+
expect(optionsLiteralNode.members[0].type.kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
248
|
+
expect(optionsLiteralNode.members[0].type.text).toBe('_');
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
describe('Relation', () => {
|
|
253
|
+
test('Basic relation', () => {
|
|
254
|
+
const attribute = { type: 'relation', relation: 'oneToOne', target: 'api::bar.bar' };
|
|
255
|
+
const typeNode = getAttributeType('foo', attribute, 'api::foo.foo');
|
|
256
|
+
|
|
257
|
+
defaultAssertions(typeNode, 'RelationAttribute');
|
|
258
|
+
|
|
259
|
+
expect(typeNode.typeArguments).toHaveLength(3);
|
|
260
|
+
|
|
261
|
+
expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
262
|
+
expect(typeNode.typeArguments[0].text).toBe('api::foo.foo');
|
|
263
|
+
|
|
264
|
+
expect(typeNode.typeArguments[1].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
265
|
+
expect(typeNode.typeArguments[1].text).toBe('oneToOne');
|
|
266
|
+
|
|
267
|
+
expect(typeNode.typeArguments[2].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
268
|
+
expect(typeNode.typeArguments[2].text).toBe('api::bar.bar');
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('Polymorphic relation', () => {
|
|
272
|
+
const attribute = { type: 'relation', relation: 'morphMany' };
|
|
273
|
+
const typeNode = getAttributeType('foo', attribute, 'api::foo.foo');
|
|
274
|
+
|
|
275
|
+
defaultAssertions(typeNode, 'RelationAttribute');
|
|
276
|
+
|
|
277
|
+
expect(typeNode.typeArguments).toHaveLength(2);
|
|
278
|
+
|
|
279
|
+
expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
280
|
+
expect(typeNode.typeArguments[0].text).toBe('api::foo.foo');
|
|
281
|
+
|
|
282
|
+
expect(typeNode.typeArguments[1].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
283
|
+
expect(typeNode.typeArguments[1].text).toBe('morphMany');
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
describe('Component', () => {
|
|
288
|
+
test('Repeatable component', () => {
|
|
289
|
+
const attribute = { type: 'component', component: 'default.comp', repeatable: true };
|
|
290
|
+
const typeNode = getAttributeType('foo', attribute);
|
|
291
|
+
|
|
292
|
+
defaultAssertions(typeNode, 'ComponentAttribute');
|
|
293
|
+
|
|
294
|
+
expect(typeNode.typeArguments).toHaveLength(2);
|
|
295
|
+
|
|
296
|
+
expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
297
|
+
expect(typeNode.typeArguments[0].text).toBe('default.comp');
|
|
298
|
+
|
|
299
|
+
expect(typeNode.typeArguments[1].kind).toBe(ts.SyntaxKind.TrueKeyword);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test('Non repeatable component', () => {
|
|
303
|
+
const attribute = { type: 'component', component: 'default.comp' };
|
|
304
|
+
const typeNode = getAttributeType('foo', attribute);
|
|
305
|
+
|
|
306
|
+
defaultAssertions(typeNode, 'ComponentAttribute');
|
|
307
|
+
|
|
308
|
+
expect(typeNode.typeArguments).toHaveLength(1);
|
|
309
|
+
|
|
310
|
+
expect(typeNode.typeArguments[0].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
311
|
+
expect(typeNode.typeArguments[0].text).toBe('default.comp');
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe('Dynamic Zone', () => {
|
|
316
|
+
test('Dynamic Zone with an array of components (targets)', () => {
|
|
317
|
+
const attribute = { type: 'dynamiczone', components: ['default.comp1', 'default.comp2'] };
|
|
318
|
+
const typeNode = getAttributeType('foo', attribute);
|
|
319
|
+
|
|
320
|
+
defaultAssertions(typeNode, 'DynamicZoneAttribute');
|
|
321
|
+
|
|
322
|
+
expect(typeNode.typeArguments).toHaveLength(1);
|
|
323
|
+
|
|
324
|
+
const [typeArgument] = typeNode.typeArguments;
|
|
325
|
+
|
|
326
|
+
expect(typeArgument.kind).toBe(ts.SyntaxKind.TupleType);
|
|
327
|
+
expect(typeArgument.elements).toHaveLength(2);
|
|
328
|
+
|
|
329
|
+
expect(typeArgument.elements[0].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
330
|
+
expect(typeArgument.elements[0].text).toBe('default.comp1');
|
|
331
|
+
|
|
332
|
+
expect(typeArgument.elements[1].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
333
|
+
expect(typeArgument.elements[1].text).toBe('default.comp2');
|
|
74
334
|
});
|
|
75
335
|
});
|
|
76
336
|
});
|
|
77
337
|
});
|
|
78
338
|
|
|
79
|
-
|
|
80
|
-
|
|
339
|
+
describe('Get Attribute Modifiers', () => {
|
|
340
|
+
describe('Units', () => {
|
|
341
|
+
describe('Required', () => {
|
|
342
|
+
test('No required', () => {
|
|
343
|
+
const attribute = {};
|
|
344
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
345
|
+
|
|
346
|
+
expect(modifiers).toHaveLength(0);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
test('Required: false', () => {
|
|
350
|
+
const attribute = { required: false };
|
|
351
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
352
|
+
|
|
353
|
+
expect(modifiers).toHaveLength(0);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
test('Required: true', () => {
|
|
357
|
+
const attribute = { required: true };
|
|
358
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
359
|
+
|
|
360
|
+
expect(modifiers).toHaveLength(1);
|
|
361
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
362
|
+
expect(modifiers[0].typeName.escapedText).toBe('RequiredAttribute');
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
describe('Private', () => {
|
|
367
|
+
test('No private', () => {
|
|
368
|
+
const attribute = {};
|
|
369
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
370
|
+
|
|
371
|
+
expect(modifiers).toHaveLength(0);
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test('Private: false', () => {
|
|
375
|
+
const attribute = { private: false };
|
|
376
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
377
|
+
|
|
378
|
+
expect(modifiers).toHaveLength(0);
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
test('Private: true', () => {
|
|
382
|
+
const attribute = { private: true };
|
|
383
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
384
|
+
|
|
385
|
+
expect(modifiers).toHaveLength(1);
|
|
386
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
387
|
+
expect(modifiers[0].typeName.escapedText).toBe('PrivateAttribute');
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
describe('Unique', () => {
|
|
392
|
+
test('No unique', () => {
|
|
393
|
+
const attribute = {};
|
|
394
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
395
|
+
|
|
396
|
+
expect(modifiers).toHaveLength(0);
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
test('Unique: false', () => {
|
|
400
|
+
const attribute = { unique: false };
|
|
401
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
402
|
+
|
|
403
|
+
expect(modifiers).toHaveLength(0);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test('Unique: true', () => {
|
|
407
|
+
const attribute = { unique: true };
|
|
408
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
409
|
+
|
|
410
|
+
expect(modifiers).toHaveLength(1);
|
|
411
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
412
|
+
expect(modifiers[0].typeName.escapedText).toBe('UniqueAttribute');
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
describe('Configurable', () => {
|
|
417
|
+
test('No configurable', () => {
|
|
418
|
+
const attribute = {};
|
|
419
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
420
|
+
|
|
421
|
+
expect(modifiers).toHaveLength(0);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test('Configurable: false', () => {
|
|
425
|
+
const attribute = { configurable: false };
|
|
426
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
427
|
+
|
|
428
|
+
expect(modifiers).toHaveLength(0);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
test('Configurable: true', () => {
|
|
432
|
+
const attribute = { configurable: true };
|
|
433
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
434
|
+
|
|
435
|
+
expect(modifiers).toHaveLength(1);
|
|
436
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
437
|
+
expect(modifiers[0].typeName.escapedText).toBe('ConfigurableAttribute');
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
describe('Plugin Options', () => {
|
|
442
|
+
test('No plugin options', () => {
|
|
443
|
+
const attribute = {};
|
|
444
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
445
|
+
|
|
446
|
+
expect(modifiers).toHaveLength(0);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
test('Plugin Options: { foo: { enabled: true } }', () => {
|
|
450
|
+
const attribute = { pluginOptions: { foo: { enabled: true } } };
|
|
451
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
452
|
+
|
|
453
|
+
expect(modifiers).toHaveLength(1);
|
|
454
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
455
|
+
expect(modifiers[0].typeName.escapedText).toBe('SetPluginOptions');
|
|
456
|
+
expect(modifiers[0].typeArguments).toHaveLength(1);
|
|
457
|
+
expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
|
|
458
|
+
expect(modifiers[0].typeArguments[0].members).toHaveLength(1);
|
|
459
|
+
expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
|
|
460
|
+
ts.SyntaxKind.PropertyDeclaration
|
|
461
|
+
);
|
|
462
|
+
expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('foo');
|
|
463
|
+
expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
|
|
464
|
+
ts.SyntaxKind.TypeLiteral
|
|
465
|
+
);
|
|
466
|
+
expect(modifiers[0].typeArguments[0].members[0].type.members).toHaveLength(1);
|
|
467
|
+
expect(modifiers[0].typeArguments[0].members[0].type.members[0].kind).toBe(
|
|
468
|
+
ts.SyntaxKind.PropertyDeclaration
|
|
469
|
+
);
|
|
470
|
+
expect(modifiers[0].typeArguments[0].members[0].type.members[0].name.escapedText).toBe(
|
|
471
|
+
'enabled'
|
|
472
|
+
);
|
|
473
|
+
expect(modifiers[0].typeArguments[0].members[0].type.members[0].type.kind).toBe(
|
|
474
|
+
ts.SyntaxKind.TrueKeyword
|
|
475
|
+
);
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
describe('Min / Max', () => {
|
|
480
|
+
test('No min or max', () => {
|
|
481
|
+
const attribute = {};
|
|
482
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
483
|
+
|
|
484
|
+
expect(modifiers).toHaveLength(0);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
test('Min: 2, no Max', () => {
|
|
488
|
+
const attribute = { min: 2 };
|
|
489
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
490
|
+
|
|
491
|
+
expect(modifiers).toHaveLength(1);
|
|
492
|
+
|
|
493
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
494
|
+
expect(modifiers[0].typeName.escapedText).toBe('SetMinMax');
|
|
495
|
+
|
|
496
|
+
expect(modifiers[0].typeArguments).toHaveLength(1);
|
|
497
|
+
expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
|
|
498
|
+
expect(modifiers[0].typeArguments[0].members).toHaveLength(1);
|
|
499
|
+
|
|
500
|
+
// Min
|
|
501
|
+
expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
|
|
502
|
+
ts.SyntaxKind.PropertyDeclaration
|
|
503
|
+
);
|
|
504
|
+
expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('min');
|
|
505
|
+
expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
|
|
506
|
+
ts.SyntaxKind.NumericLiteral
|
|
507
|
+
);
|
|
508
|
+
expect(modifiers[0].typeArguments[0].members[0].type.text).toBe('2');
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
test('No Min, Max: 3', () => {
|
|
512
|
+
const attribute = { max: 3 };
|
|
513
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
514
|
+
|
|
515
|
+
expect(modifiers).toHaveLength(1);
|
|
516
|
+
|
|
517
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
518
|
+
expect(modifiers[0].typeName.escapedText).toBe('SetMinMax');
|
|
519
|
+
|
|
520
|
+
expect(modifiers[0].typeArguments).toHaveLength(1);
|
|
521
|
+
expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
|
|
522
|
+
expect(modifiers[0].typeArguments[0].members).toHaveLength(1);
|
|
523
|
+
|
|
524
|
+
// Min
|
|
525
|
+
expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
|
|
526
|
+
ts.SyntaxKind.PropertyDeclaration
|
|
527
|
+
);
|
|
528
|
+
expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('max');
|
|
529
|
+
expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
|
|
530
|
+
ts.SyntaxKind.NumericLiteral
|
|
531
|
+
);
|
|
532
|
+
expect(modifiers[0].typeArguments[0].members[0].type.text).toBe('3');
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
test('Min: 4, Max: 12', () => {
|
|
536
|
+
const attribute = { min: 4, max: 12 };
|
|
537
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
538
|
+
|
|
539
|
+
expect(modifiers).toHaveLength(1);
|
|
540
|
+
|
|
541
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
542
|
+
expect(modifiers[0].typeName.escapedText).toBe('SetMinMax');
|
|
543
|
+
|
|
544
|
+
expect(modifiers[0].typeArguments).toHaveLength(1);
|
|
545
|
+
expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
|
|
546
|
+
expect(modifiers[0].typeArguments[0].members).toHaveLength(2);
|
|
547
|
+
|
|
548
|
+
// Min
|
|
549
|
+
expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
|
|
550
|
+
ts.SyntaxKind.PropertyDeclaration
|
|
551
|
+
);
|
|
552
|
+
expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('min');
|
|
553
|
+
expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
|
|
554
|
+
ts.SyntaxKind.NumericLiteral
|
|
555
|
+
);
|
|
556
|
+
expect(modifiers[0].typeArguments[0].members[0].type.text).toBe('4');
|
|
557
|
+
|
|
558
|
+
expect(modifiers[0].typeArguments[0].members[1].kind).toBe(
|
|
559
|
+
ts.SyntaxKind.PropertyDeclaration
|
|
560
|
+
);
|
|
561
|
+
expect(modifiers[0].typeArguments[0].members[1].name.escapedText).toBe('max');
|
|
562
|
+
expect(modifiers[0].typeArguments[0].members[1].type.kind).toBe(
|
|
563
|
+
ts.SyntaxKind.NumericLiteral
|
|
564
|
+
);
|
|
565
|
+
expect(modifiers[0].typeArguments[0].members[1].type.text).toBe('12');
|
|
566
|
+
});
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
describe('MinLength / MaxLength', () => {
|
|
570
|
+
test('No minLength or maxLength', () => {
|
|
571
|
+
const attribute = {};
|
|
572
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
573
|
+
|
|
574
|
+
expect(modifiers).toHaveLength(0);
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
test('MinLength: 2, no MaxLength', () => {
|
|
578
|
+
const attribute = { minLength: 2 };
|
|
579
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
580
|
+
|
|
581
|
+
expect(modifiers).toHaveLength(1);
|
|
582
|
+
|
|
583
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
584
|
+
expect(modifiers[0].typeName.escapedText).toBe('SetMinMaxLength');
|
|
585
|
+
|
|
586
|
+
expect(modifiers[0].typeArguments).toHaveLength(1);
|
|
587
|
+
expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
|
|
588
|
+
expect(modifiers[0].typeArguments[0].members).toHaveLength(1);
|
|
589
|
+
|
|
590
|
+
// Min
|
|
591
|
+
expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
|
|
592
|
+
ts.SyntaxKind.PropertyDeclaration
|
|
593
|
+
);
|
|
594
|
+
expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('minLength');
|
|
595
|
+
expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
|
|
596
|
+
ts.SyntaxKind.NumericLiteral
|
|
597
|
+
);
|
|
598
|
+
expect(modifiers[0].typeArguments[0].members[0].type.text).toBe('2');
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
test('No MinLength, MaxLength: 3', () => {
|
|
602
|
+
const attribute = { maxLength: 3 };
|
|
603
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
604
|
+
|
|
605
|
+
expect(modifiers).toHaveLength(1);
|
|
606
|
+
|
|
607
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
608
|
+
expect(modifiers[0].typeName.escapedText).toBe('SetMinMaxLength');
|
|
609
|
+
|
|
610
|
+
expect(modifiers[0].typeArguments).toHaveLength(1);
|
|
611
|
+
expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
|
|
612
|
+
expect(modifiers[0].typeArguments[0].members).toHaveLength(1);
|
|
613
|
+
|
|
614
|
+
// Min
|
|
615
|
+
expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
|
|
616
|
+
ts.SyntaxKind.PropertyDeclaration
|
|
617
|
+
);
|
|
618
|
+
expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('maxLength');
|
|
619
|
+
expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
|
|
620
|
+
ts.SyntaxKind.NumericLiteral
|
|
621
|
+
);
|
|
622
|
+
expect(modifiers[0].typeArguments[0].members[0].type.text).toBe('3');
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
test('MinLength: 4, MaxLength: 12', () => {
|
|
626
|
+
const attribute = { minLength: 4, maxLength: 12 };
|
|
627
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
628
|
+
|
|
629
|
+
expect(modifiers).toHaveLength(1);
|
|
630
|
+
|
|
631
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
632
|
+
expect(modifiers[0].typeName.escapedText).toBe('SetMinMaxLength');
|
|
633
|
+
|
|
634
|
+
expect(modifiers[0].typeArguments).toHaveLength(1);
|
|
635
|
+
expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
|
|
636
|
+
expect(modifiers[0].typeArguments[0].members).toHaveLength(2);
|
|
637
|
+
|
|
638
|
+
// Min
|
|
639
|
+
expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
|
|
640
|
+
ts.SyntaxKind.PropertyDeclaration
|
|
641
|
+
);
|
|
642
|
+
expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('minLength');
|
|
643
|
+
expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
|
|
644
|
+
ts.SyntaxKind.NumericLiteral
|
|
645
|
+
);
|
|
646
|
+
expect(modifiers[0].typeArguments[0].members[0].type.text).toBe('4');
|
|
647
|
+
|
|
648
|
+
expect(modifiers[0].typeArguments[0].members[1].kind).toBe(
|
|
649
|
+
ts.SyntaxKind.PropertyDeclaration
|
|
650
|
+
);
|
|
651
|
+
expect(modifiers[0].typeArguments[0].members[1].name.escapedText).toBe('maxLength');
|
|
652
|
+
expect(modifiers[0].typeArguments[0].members[1].type.kind).toBe(
|
|
653
|
+
ts.SyntaxKind.NumericLiteral
|
|
654
|
+
);
|
|
655
|
+
expect(modifiers[0].typeArguments[0].members[1].type.text).toBe('12');
|
|
656
|
+
});
|
|
657
|
+
});
|
|
658
|
+
|
|
659
|
+
describe('Default', () => {
|
|
660
|
+
test('No default', () => {
|
|
661
|
+
const attribute = {};
|
|
662
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
663
|
+
|
|
664
|
+
expect(modifiers).toHaveLength(0);
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
test('Default: true', () => {
|
|
668
|
+
const attribute = { default: true };
|
|
669
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
670
|
+
|
|
671
|
+
expect(modifiers).toHaveLength(1);
|
|
672
|
+
|
|
673
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
674
|
+
expect(modifiers[0].typeName.escapedText).toBe('DefaultTo');
|
|
675
|
+
|
|
676
|
+
expect(modifiers[0].typeArguments).toHaveLength(1);
|
|
677
|
+
expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TrueKeyword);
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
test('Default: { enabled: true }', () => {
|
|
681
|
+
const attribute = { default: { enabled: true } };
|
|
682
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
683
|
+
|
|
684
|
+
expect(modifiers).toHaveLength(1);
|
|
685
|
+
|
|
686
|
+
expect(modifiers[0].kind).toBe(ts.SyntaxKind.TypeReference);
|
|
687
|
+
expect(modifiers[0].typeName.escapedText).toBe('DefaultTo');
|
|
688
|
+
|
|
689
|
+
expect(modifiers[0].typeArguments).toHaveLength(1);
|
|
690
|
+
expect(modifiers[0].typeArguments[0].kind).toBe(ts.SyntaxKind.TypeLiteral);
|
|
691
|
+
expect(modifiers[0].typeArguments[0].members).toHaveLength(1);
|
|
692
|
+
expect(modifiers[0].typeArguments[0].members[0].kind).toBe(
|
|
693
|
+
ts.SyntaxKind.PropertyDeclaration
|
|
694
|
+
);
|
|
695
|
+
expect(modifiers[0].typeArguments[0].members[0].name.escapedText).toBe('enabled');
|
|
696
|
+
expect(modifiers[0].typeArguments[0].members[0].type.kind).toBe(
|
|
697
|
+
ts.SyntaxKind.TrueKeyword
|
|
698
|
+
);
|
|
699
|
+
});
|
|
700
|
+
});
|
|
701
|
+
});
|
|
702
|
+
});
|
|
81
703
|
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
jest.mock('../../../generators/schemas/utils', () => ({
|
|
4
|
+
getSchemaInterfaceName: jest.fn(),
|
|
5
|
+
}));
|
|
6
|
+
|
|
7
|
+
const ts = require('typescript');
|
|
8
|
+
const { get } = require('lodash/fp');
|
|
9
|
+
|
|
10
|
+
const { generateGlobalDefinition } = require('../../../generators/schemas/global');
|
|
11
|
+
const { getSchemaInterfaceName } = require('../../../generators/schemas/utils');
|
|
12
|
+
|
|
13
|
+
const getSchemasInterfaceNode = get('body.statements[0].body.statements[0]');
|
|
14
|
+
|
|
15
|
+
describe('Global', () => {
|
|
16
|
+
afterAll(() => {
|
|
17
|
+
jest.resetAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const assertGlobalNodeStructure = node => {
|
|
21
|
+
// "declare global"
|
|
22
|
+
expect(node.kind).toBe(ts.SyntaxKind.ModuleDeclaration);
|
|
23
|
+
expect(node.modifiers).toHaveLength(1);
|
|
24
|
+
expect(node.modifiers[0].kind).toBe(ts.SyntaxKind.DeclareKeyword);
|
|
25
|
+
expect(node.name.originalKeywordKind).toBe(ts.SyntaxKind.GlobalKeyword);
|
|
26
|
+
expect(node.name.escapedText).toBe('global');
|
|
27
|
+
|
|
28
|
+
// "namespace Strapi"
|
|
29
|
+
const [strapiNamespace] = node.body.statements;
|
|
30
|
+
|
|
31
|
+
expect(strapiNamespace.kind).toBe(ts.SyntaxKind.ModuleDeclaration);
|
|
32
|
+
expect(strapiNamespace.name.kind).toBe(ts.SyntaxKind.Identifier);
|
|
33
|
+
expect(strapiNamespace.name.escapedText).toBe('Strapi');
|
|
34
|
+
|
|
35
|
+
// "interface Schemas"
|
|
36
|
+
const [schemasInterface] = strapiNamespace.body.statements;
|
|
37
|
+
|
|
38
|
+
expect(schemasInterface.kind).toBe(ts.SyntaxKind.InterfaceDeclaration);
|
|
39
|
+
expect(schemasInterface.name.escapedText).toBe('Schemas');
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
describe('Generate Global Definition', () => {
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
jest.resetAllMocks();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('With empty definition', () => {
|
|
48
|
+
const definitions = [];
|
|
49
|
+
|
|
50
|
+
const globalNode = generateGlobalDefinition(definitions);
|
|
51
|
+
|
|
52
|
+
assertGlobalNodeStructure(globalNode);
|
|
53
|
+
|
|
54
|
+
expect(getSchemaInterfaceName).not.toHaveBeenCalled();
|
|
55
|
+
|
|
56
|
+
const schemasNode = getSchemasInterfaceNode(globalNode);
|
|
57
|
+
|
|
58
|
+
expect(schemasNode.members).toHaveLength(0);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('With no definition', () => {
|
|
62
|
+
const globalNode = generateGlobalDefinition();
|
|
63
|
+
|
|
64
|
+
assertGlobalNodeStructure(globalNode);
|
|
65
|
+
|
|
66
|
+
expect(getSchemaInterfaceName).not.toHaveBeenCalled();
|
|
67
|
+
|
|
68
|
+
const schemasNode = getSchemasInterfaceNode(globalNode);
|
|
69
|
+
|
|
70
|
+
expect(schemasNode.members).toHaveLength(0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('With multiple definitions', () => {
|
|
74
|
+
const definitions = [
|
|
75
|
+
{ schema: { uid: 'api::foo.foo' } },
|
|
76
|
+
{ schema: { uid: 'api::bar.bar' } },
|
|
77
|
+
{ schema: { uid: 'api::foobar.foobar' } },
|
|
78
|
+
{ schema: { uid: 'default.barfoo' } },
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
getSchemaInterfaceName.mockReturnValue('Placeholder');
|
|
82
|
+
|
|
83
|
+
const globalNode = generateGlobalDefinition(definitions);
|
|
84
|
+
|
|
85
|
+
assertGlobalNodeStructure(globalNode);
|
|
86
|
+
|
|
87
|
+
const schemasNode = getSchemasInterfaceNode(globalNode);
|
|
88
|
+
|
|
89
|
+
expect(schemasNode.members).toHaveLength(definitions.length);
|
|
90
|
+
|
|
91
|
+
definitions.forEach(({ schema }, index) => {
|
|
92
|
+
const { uid } = schema;
|
|
93
|
+
const node = schemasNode.members[index];
|
|
94
|
+
|
|
95
|
+
expect(node.kind).toBe(ts.SyntaxKind.PropertySignature);
|
|
96
|
+
|
|
97
|
+
expect(getSchemaInterfaceName).toHaveBeenCalledWith(uid);
|
|
98
|
+
|
|
99
|
+
expect(node.name.kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
100
|
+
expect(node.name.text).toBe(uid);
|
|
101
|
+
expect(node.name.singleQuote).toBeTruthy();
|
|
102
|
+
|
|
103
|
+
expect(node.type.kind).toBe(ts.SyntaxKind.TypeReference);
|
|
104
|
+
expect(node.type.typeName.escapedText).toBe('Placeholder');
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
});
|
|
@@ -22,7 +22,7 @@ const attributeToPropertySignature = (schema, attributeName, attribute) => {
|
|
|
22
22
|
return null;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const modifiers = getAttributeModifiers(
|
|
25
|
+
const modifiers = getAttributeModifiers(attribute);
|
|
26
26
|
|
|
27
27
|
const nodes = [baseType, ...modifiers];
|
|
28
28
|
|
|
@@ -61,11 +61,10 @@ const getAttributeType = (attributeName, attribute, uid) => {
|
|
|
61
61
|
/**
|
|
62
62
|
* Collect every modifier node from an attribute
|
|
63
63
|
*
|
|
64
|
-
* @param {string} _attributeName
|
|
65
64
|
* @param {object} attribute
|
|
66
65
|
* @returns {object[]}
|
|
67
66
|
*/
|
|
68
|
-
const getAttributeModifiers =
|
|
67
|
+
const getAttributeModifiers = attribute => {
|
|
69
68
|
const modifiers = [];
|
|
70
69
|
|
|
71
70
|
// Required
|
|
@@ -112,6 +111,7 @@ const getAttributeModifiers = (_attributeName, attribute) => {
|
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
// Min / Max
|
|
114
|
+
// TODO: Always provide a second type argument for min/max (ie: resolve the attribute scalar type with a `GetAttributeType<${mappers[attribute][0]}>` (useful for biginter (string values)))
|
|
115
115
|
if (!_.isNil(attribute.min) || !_.isNil(attribute.max)) {
|
|
116
116
|
addImport('SetMinMax');
|
|
117
117
|
|
|
@@ -223,11 +223,7 @@ const mappers = {
|
|
|
223
223
|
enumeration({ attribute }) {
|
|
224
224
|
const { enum: enumValues } = attribute;
|
|
225
225
|
|
|
226
|
-
|
|
227
|
-
return ['EnumerationAttribute', [toTypeLiteral(enumValues)]];
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
return ['EnumerationAttribute'];
|
|
226
|
+
return ['EnumerationAttribute', [toTypeLiteral(enumValues)]];
|
|
231
227
|
},
|
|
232
228
|
boolean() {
|
|
233
229
|
return ['BooleanAttribute'];
|
|
@@ -34,7 +34,13 @@ const DEFAULT_OUT_FILENAME = 'schemas.d.ts';
|
|
|
34
34
|
* @param {boolean} [options.verbose]
|
|
35
35
|
*/
|
|
36
36
|
const generateSchemasDefinitions = async (options = {}) => {
|
|
37
|
-
const {
|
|
37
|
+
const {
|
|
38
|
+
strapi,
|
|
39
|
+
outDir = process.cwd(),
|
|
40
|
+
file = DEFAULT_OUT_FILENAME,
|
|
41
|
+
verbose = false,
|
|
42
|
+
silent = false,
|
|
43
|
+
} = options;
|
|
38
44
|
|
|
39
45
|
const schemas = getAllStrapiSchemas(strapi);
|
|
40
46
|
|
|
@@ -74,9 +80,7 @@ const generateSchemasDefinitions = async (options = {}) => {
|
|
|
74
80
|
|
|
75
81
|
const definitionFilepath = await saveDefinitionToFileSystem(outDir, file, formattedOutput);
|
|
76
82
|
|
|
77
|
-
|
|
78
|
-
logDebugInformation(schemasDefinitions, { filepath: definitionFilepath });
|
|
79
|
-
}
|
|
83
|
+
logDebugInformation(schemasDefinitions, { filepath: definitionFilepath, verbose, silent });
|
|
80
84
|
};
|
|
81
85
|
|
|
82
86
|
const emitDefinitions = definitions => {
|
|
@@ -108,7 +112,7 @@ const saveDefinitionToFileSystem = async (dir, file, content) => {
|
|
|
108
112
|
* Uses the existing config if one is defined in the project.
|
|
109
113
|
*
|
|
110
114
|
* @param {string} content
|
|
111
|
-
* @returns {string}
|
|
115
|
+
* @returns {Promise<string>}
|
|
112
116
|
*/
|
|
113
117
|
const format = async content => {
|
|
114
118
|
const configFile = await prettier.resolveConfigFile();
|
|
@@ -127,51 +131,55 @@ const format = async content => {
|
|
|
127
131
|
};
|
|
128
132
|
|
|
129
133
|
const logDebugInformation = (definitions, options = {}) => {
|
|
130
|
-
const { filepath } = options;
|
|
131
|
-
|
|
132
|
-
const table = new CLITable({
|
|
133
|
-
head: [
|
|
134
|
-
chalk.bold(chalk.green('Model Type')),
|
|
135
|
-
chalk.bold(chalk.blue('UID')),
|
|
136
|
-
chalk.bold(chalk.blue('Type')),
|
|
137
|
-
chalk.bold(chalk.gray('Attributes Count')),
|
|
138
|
-
],
|
|
139
|
-
colAligns: ['center', 'left', 'left', 'center'],
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
const sortedDefinitions = definitions.map(def => ({
|
|
143
|
-
...def,
|
|
144
|
-
attributesCount: getDefinitionAttributesCount(def.definition),
|
|
145
|
-
}));
|
|
134
|
+
const { filepath, verbose, silent } = options;
|
|
146
135
|
|
|
147
|
-
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
136
|
+
if (verbose) {
|
|
137
|
+
const table = new CLITable({
|
|
138
|
+
head: [
|
|
139
|
+
chalk.bold(chalk.green('Model Type')),
|
|
140
|
+
chalk.bold(chalk.blue('UID')),
|
|
141
|
+
chalk.bold(chalk.blue('Type')),
|
|
142
|
+
chalk.bold(chalk.gray('Attributes Count')),
|
|
143
|
+
],
|
|
144
|
+
colAligns: ['center', 'left', 'left', 'center'],
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const sortedDefinitions = definitions.map(def => ({
|
|
148
|
+
...def,
|
|
149
|
+
attributesCount: getDefinitionAttributesCount(def.definition),
|
|
150
|
+
}));
|
|
151
|
+
|
|
152
|
+
for (const { schema, attributesCount } of sortedDefinitions) {
|
|
153
|
+
const modelType = fp.upperFirst(getSchemaModelType(schema));
|
|
154
|
+
const interfaceType = getSchemaInterfaceName(schema.uid);
|
|
155
|
+
|
|
156
|
+
table.push([
|
|
157
|
+
chalk.greenBright(modelType),
|
|
158
|
+
chalk.blue(schema.uid),
|
|
159
|
+
chalk.blue(interfaceType),
|
|
160
|
+
chalk.grey(fp.isNil(attributesCount) ? 'N/A' : attributesCount),
|
|
161
|
+
]);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Table
|
|
165
|
+
console.log(table.toString());
|
|
157
166
|
}
|
|
158
167
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
)
|
|
167
|
-
);
|
|
168
|
+
if (!silent) {
|
|
169
|
+
// Metrics
|
|
170
|
+
console.log(
|
|
171
|
+
chalk.greenBright(
|
|
172
|
+
`Generated ${definitions.length} type definition for your Strapi application's schemas.`
|
|
173
|
+
)
|
|
174
|
+
);
|
|
168
175
|
|
|
169
|
-
|
|
170
|
-
|
|
176
|
+
// Filepath
|
|
177
|
+
const relativePath = path.relative(process.cwd(), filepath);
|
|
171
178
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
179
|
+
console.log(
|
|
180
|
+
chalk.grey(`The definitions file has been generated here: ${chalk.bold(relativePath)}`)
|
|
181
|
+
);
|
|
182
|
+
}
|
|
175
183
|
};
|
|
176
184
|
|
|
177
185
|
module.exports = generateSchemasDefinitions;
|
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
isNull,
|
|
12
12
|
isString,
|
|
13
13
|
isNumber,
|
|
14
|
+
isDate,
|
|
14
15
|
isArray,
|
|
15
16
|
isBoolean,
|
|
16
17
|
propEq,
|
|
@@ -101,6 +102,10 @@ const toTypeLiteral = data => {
|
|
|
101
102
|
return factory.createTupleTypeNode(data.map(item => toTypeLiteral(item)));
|
|
102
103
|
}
|
|
103
104
|
|
|
105
|
+
if (isDate(data)) {
|
|
106
|
+
return factory.createStringLiteral(data.toISOString());
|
|
107
|
+
}
|
|
108
|
+
|
|
104
109
|
if (typeof data !== 'object') {
|
|
105
110
|
throw new Error(`Cannot convert to object literal. Unknown type "${typeof data}"`);
|
|
106
111
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@strapi/typescript-utils",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.2-alpha.0",
|
|
4
4
|
"description": "Typescript support for Strapi",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"strapi",
|
|
@@ -35,5 +35,5 @@
|
|
|
35
35
|
"node": ">=12.22.0 <=16.x.x",
|
|
36
36
|
"npm": ">=6.0.0"
|
|
37
37
|
},
|
|
38
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "8ab2db1497c3a5a805173b2d92248648b40a6f65"
|
|
39
39
|
}
|