@strapi/typescript-utils 0.0.0-next.f7babb775ed9a7e18d8351cb7f74c63e016323c4 → 0.0.0-next.f86041c89a8c1545c6437a881dc613e98bc52bd7
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/LICENSE +18 -3
- package/lib/__tests__/generators/schemas/attributes.test.js +280 -135
- package/lib/__tests__/generators/schemas/utils.test.js +40 -34
- package/lib/compile.js +2 -6
- package/lib/compilers/basic.js +12 -4
- package/lib/compilers/index.js +0 -2
- package/lib/generators/common/imports.js +3 -3
- package/lib/generators/common/models/attributes.js +35 -10
- package/lib/generators/common/models/mappers.js +35 -22
- package/lib/generators/common/models/schema.js +7 -5
- package/lib/generators/common/models/utils.js +13 -8
- package/lib/generators/components/index.js +21 -5
- package/lib/generators/content-types/index.js +21 -5
- package/lib/generators/index.js +1 -1
- package/lib/generators/utils.js +10 -5
- package/lib/index.js +0 -3
- package/lib/utils/index.js +2 -0
- package/lib/utils/resolve-outdir-sync.js +18 -0
- package/package.json +15 -8
- package/tsconfigs/admin.json +18 -19
- package/tsconfigs/server.json +18 -16
- package/lib/admin/create-tsconfig-file.js +0 -37
- package/lib/admin/index.js +0 -5
- package/lib/compilers/watch.js +0 -37
|
@@ -120,9 +120,9 @@ describe('Utils', () => {
|
|
|
120
120
|
|
|
121
121
|
describe('Get Schema Extends Type Name', () => {
|
|
122
122
|
test.each([
|
|
123
|
-
[{ modelType: 'component', kind: null }, '
|
|
124
|
-
[{ modelType: 'contentType', kind: 'singleType' }, '
|
|
125
|
-
[{ modelType: 'contentType', kind: 'collectionType' }, '
|
|
123
|
+
[{ modelType: 'component', kind: null }, 'Struct.ComponentSchema'],
|
|
124
|
+
[{ modelType: 'contentType', kind: 'singleType' }, 'Struct.SingleTypeSchema'],
|
|
125
|
+
[{ modelType: 'contentType', kind: 'collectionType' }, 'Struct.CollectionTypeSchema'],
|
|
126
126
|
[{ modelType: 'invalidType', kind: 'foo' }, null],
|
|
127
127
|
])("Expect %p to generate %p as the base type for a schema's interface", (schema, expected) => {
|
|
128
128
|
expect(getSchemaExtendsTypeName(schema)).toBe(expected);
|
|
@@ -168,10 +168,16 @@ describe('Utils', () => {
|
|
|
168
168
|
});
|
|
169
169
|
|
|
170
170
|
test('Number', () => {
|
|
171
|
-
const
|
|
171
|
+
const nodePositive = toTypeLiteral(42);
|
|
172
|
+
const nodeNegative = toTypeLiteral(-42);
|
|
172
173
|
|
|
173
|
-
expect(
|
|
174
|
-
expect(
|
|
174
|
+
expect(nodePositive.kind).toBe(ts.SyntaxKind.FirstLiteralToken);
|
|
175
|
+
expect(nodePositive.text).toBe('42');
|
|
176
|
+
|
|
177
|
+
expect(nodeNegative.kind).toBe(ts.SyntaxKind.PrefixUnaryExpression);
|
|
178
|
+
expect(nodeNegative.operator).toBe(ts.SyntaxKind.MinusToken);
|
|
179
|
+
expect(nodeNegative.operand.kind).toBe(ts.SyntaxKind.FirstLiteralToken);
|
|
180
|
+
expect(nodeNegative.operand.text).toBe('42');
|
|
175
181
|
});
|
|
176
182
|
|
|
177
183
|
test('Boolean', () => {
|
|
@@ -247,13 +253,13 @@ describe('Utils', () => {
|
|
|
247
253
|
expect(objectNode.members).toHaveLength(2);
|
|
248
254
|
|
|
249
255
|
expect(objectNode.members[0].kind).toBe(ts.SyntaxKind.PropertyDeclaration);
|
|
250
|
-
expect(objectNode.members[0].name.escapedText).toBe('
|
|
251
|
-
expect(objectNode.members[0].type.kind).toBe(ts.SyntaxKind.
|
|
252
|
-
expect(objectNode.members[0].type.text).toBe('bar');
|
|
256
|
+
expect(objectNode.members[0].name.escapedText).toBe('bar');
|
|
257
|
+
expect(objectNode.members[0].type.kind).toBe(ts.SyntaxKind.TrueKeyword);
|
|
253
258
|
|
|
254
259
|
expect(objectNode.members[1].kind).toBe(ts.SyntaxKind.PropertyDeclaration);
|
|
255
|
-
expect(objectNode.members[1].name.escapedText).toBe('
|
|
256
|
-
expect(objectNode.members[1].type.kind).toBe(ts.SyntaxKind.
|
|
260
|
+
expect(objectNode.members[1].name.escapedText).toBe('foo');
|
|
261
|
+
expect(objectNode.members[1].type.kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
262
|
+
expect(objectNode.members[1].type.text).toBe('bar');
|
|
257
263
|
});
|
|
258
264
|
|
|
259
265
|
test('Object', () => {
|
|
@@ -262,20 +268,20 @@ describe('Utils', () => {
|
|
|
262
268
|
expect(node.kind).toBe(ts.SyntaxKind.TypeLiteral);
|
|
263
269
|
expect(node.members).toHaveLength(2);
|
|
264
270
|
|
|
265
|
-
const [
|
|
271
|
+
const [barMember, fooMember] = node.members;
|
|
266
272
|
|
|
267
|
-
expect(
|
|
268
|
-
expect(
|
|
269
|
-
expect(
|
|
270
|
-
expect(
|
|
271
|
-
expect(firstMember.type.elements[0].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
272
|
-
expect(firstMember.type.elements[1].kind).toBe(ts.SyntaxKind.TrueKeyword);
|
|
273
|
-
expect(firstMember.type.elements[2].kind).toBe(ts.SyntaxKind.FirstLiteralToken);
|
|
273
|
+
expect(barMember.kind).toBe(ts.SyntaxKind.PropertyDeclaration);
|
|
274
|
+
expect(barMember.name.escapedText).toBe('bar');
|
|
275
|
+
expect(barMember.type.kind).toBe(ts.SyntaxKind.LiteralType);
|
|
276
|
+
expect(barMember.type.literal).toBe(ts.SyntaxKind.NullKeyword);
|
|
274
277
|
|
|
275
|
-
expect(
|
|
276
|
-
expect(
|
|
277
|
-
expect(
|
|
278
|
-
expect(
|
|
278
|
+
expect(fooMember.kind).toBe(ts.SyntaxKind.PropertyDeclaration);
|
|
279
|
+
expect(fooMember.name.escapedText).toBe('foo');
|
|
280
|
+
expect(fooMember.type.kind).toBe(ts.SyntaxKind.TupleType);
|
|
281
|
+
expect(fooMember.type.elements).toHaveLength(3);
|
|
282
|
+
expect(fooMember.type.elements[0].kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
283
|
+
expect(fooMember.type.elements[1].kind).toBe(ts.SyntaxKind.TrueKeyword);
|
|
284
|
+
expect(fooMember.type.elements[2].kind).toBe(ts.SyntaxKind.FirstLiteralToken);
|
|
279
285
|
});
|
|
280
286
|
|
|
281
287
|
test('Object with complex keys', () => {
|
|
@@ -284,19 +290,19 @@ describe('Utils', () => {
|
|
|
284
290
|
expect(node.kind).toBe(ts.SyntaxKind.TypeLiteral);
|
|
285
291
|
expect(node.members).toHaveLength(2);
|
|
286
292
|
|
|
287
|
-
const [
|
|
293
|
+
const [fooBar, fooDashBar] = node.members;
|
|
288
294
|
|
|
289
|
-
expect(
|
|
290
|
-
expect(
|
|
291
|
-
expect(
|
|
292
|
-
expect(
|
|
293
|
-
expect(
|
|
295
|
+
expect(fooBar.kind).toBe(ts.SyntaxKind.PropertyDeclaration);
|
|
296
|
+
expect(fooBar.name.kind).toBe(ts.SyntaxKind.Identifier);
|
|
297
|
+
expect(fooBar.name.escapedText).toBe('foo');
|
|
298
|
+
expect(fooBar.type.kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
299
|
+
expect(fooBar.type.text).toBe('bar');
|
|
294
300
|
|
|
295
|
-
expect(
|
|
296
|
-
expect(
|
|
297
|
-
expect(
|
|
298
|
-
expect(
|
|
299
|
-
expect(
|
|
301
|
+
expect(fooDashBar.kind).toBe(ts.SyntaxKind.PropertyDeclaration);
|
|
302
|
+
expect(fooDashBar.name.kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
303
|
+
expect(fooDashBar.name.text).toBe('foo-bar');
|
|
304
|
+
expect(fooDashBar.type.kind).toBe(ts.SyntaxKind.StringLiteral);
|
|
305
|
+
expect(fooDashBar.type.text).toBe('foobar');
|
|
300
306
|
});
|
|
301
307
|
|
|
302
308
|
test('Invalid data type supplied (function)', () => {
|
package/lib/compile.js
CHANGED
|
@@ -3,12 +3,8 @@
|
|
|
3
3
|
const compilers = require('./compilers');
|
|
4
4
|
const getConfigPath = require('./utils/get-config-path');
|
|
5
5
|
|
|
6
|
-
module.exports = async (srcDir, {
|
|
7
|
-
// TODO: Use the Strapi debug logger instead or don't log at all
|
|
8
|
-
console.log(`Starting the compilation for TypeScript files in ${srcDir}`);
|
|
9
|
-
|
|
10
|
-
const compiler = watch ? compilers.watch : compilers.basic;
|
|
6
|
+
module.exports = async (srcDir, { configOptions = {} } = {}) => {
|
|
11
7
|
const configPath = getConfigPath(srcDir);
|
|
12
8
|
|
|
13
|
-
|
|
9
|
+
compilers.basic.run(configPath, configOptions);
|
|
14
10
|
};
|
package/lib/compilers/basic.js
CHANGED
|
@@ -13,15 +13,23 @@ module.exports = {
|
|
|
13
13
|
* @param {Object} configOptions
|
|
14
14
|
* @param {Array.<string>} configOptions.fileNames
|
|
15
15
|
* @param {Object} configOptions.options
|
|
16
|
+
* @param {boolean} configOptions.ignoreDiagnostics
|
|
16
17
|
*/
|
|
17
18
|
run(tsConfigPath, configOptions = {}) {
|
|
19
|
+
const { ignoreDiagnostics = false } = configOptions;
|
|
18
20
|
// Parse the tsconfig.json file & resolve the configuration options
|
|
19
21
|
const { fileNames, options, projectReferences } = resolveConfigOptions(tsConfigPath);
|
|
20
22
|
|
|
23
|
+
const compilerOptions = merge(options, configOptions.options);
|
|
24
|
+
|
|
25
|
+
if (ignoreDiagnostics) {
|
|
26
|
+
Object.assign(compilerOptions, { noEmit: false, noEmitOnError: false });
|
|
27
|
+
}
|
|
28
|
+
|
|
21
29
|
const program = ts.createProgram({
|
|
22
30
|
rootNames: configOptions.fileNames ? configOptions.fileNames : fileNames,
|
|
23
31
|
projectReferences,
|
|
24
|
-
options:
|
|
32
|
+
options: compilerOptions,
|
|
25
33
|
});
|
|
26
34
|
|
|
27
35
|
const emitResults = program.emit();
|
|
@@ -30,12 +38,12 @@ module.exports = {
|
|
|
30
38
|
ts.getPreEmitDiagnostics(program).concat(emitResults.diagnostics)
|
|
31
39
|
);
|
|
32
40
|
|
|
33
|
-
if (diagnostics.length > 0) {
|
|
41
|
+
if (!ignoreDiagnostics && diagnostics.length > 0) {
|
|
34
42
|
reportDiagnostics(diagnostics);
|
|
35
43
|
}
|
|
36
44
|
|
|
37
|
-
// If the compilation failed, exit early
|
|
38
|
-
if (emitResults.emitSkipped) {
|
|
45
|
+
// If the compilation failed and diagnostics are not ignored, exit early
|
|
46
|
+
if (!ignoreDiagnostics && emitResults.emitSkipped) {
|
|
39
47
|
process.exit(1);
|
|
40
48
|
}
|
|
41
49
|
},
|
package/lib/compilers/index.js
CHANGED
|
@@ -18,9 +18,9 @@ module.exports = {
|
|
|
18
18
|
},
|
|
19
19
|
|
|
20
20
|
generateImportDefinition() {
|
|
21
|
-
const formattedImports = imports
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const formattedImports = imports
|
|
22
|
+
.sort()
|
|
23
|
+
.map((key) => factory.createImportSpecifier(false, undefined, factory.createIdentifier(key)));
|
|
24
24
|
|
|
25
25
|
return [
|
|
26
26
|
factory.createImportDeclaration(
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const ts = require('typescript');
|
|
4
4
|
const _ = require('lodash/fp');
|
|
5
5
|
|
|
6
6
|
const { addImport } = require('../imports');
|
|
7
7
|
const { getTypeNode, toTypeLiteral, withAttributeNamespace, NAMESPACES } = require('./utils');
|
|
8
8
|
const mappers = require('./mappers');
|
|
9
9
|
|
|
10
|
+
const { factory } = ts;
|
|
11
|
+
|
|
10
12
|
/**
|
|
11
13
|
* Create the base type node for a given attribute
|
|
12
14
|
*
|
|
@@ -26,8 +28,8 @@ const getAttributeType = (attributeName, attribute, uid) => {
|
|
|
26
28
|
|
|
27
29
|
const [attributeType, typeParams] = mappers[attribute.type]({ uid, attribute, attributeName });
|
|
28
30
|
|
|
29
|
-
// Make sure the
|
|
30
|
-
addImport(NAMESPACES.
|
|
31
|
+
// Make sure the schema namespace is imported
|
|
32
|
+
addImport(NAMESPACES.Schema);
|
|
31
33
|
|
|
32
34
|
return getTypeNode(attributeType, typeParams);
|
|
33
35
|
};
|
|
@@ -100,14 +102,39 @@ const getAttributeModifiers = (attribute) => {
|
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
// Min / Max
|
|
103
|
-
// 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)))
|
|
104
105
|
if (!_.isNil(attribute.min) || !_.isNil(attribute.max)) {
|
|
105
106
|
const minMaxProperties = _.pick(['min', 'max'], attribute);
|
|
107
|
+
const { min, max } = minMaxProperties;
|
|
108
|
+
|
|
109
|
+
const typeofMin = typeof min;
|
|
110
|
+
const typeofMax = typeof max;
|
|
111
|
+
|
|
112
|
+
// Throws error if min/max exist but have different types to prevent unexpected behavior
|
|
113
|
+
if (min !== undefined && max !== undefined && typeofMin !== typeofMax) {
|
|
114
|
+
throw new Error('typeof min/max values mismatch');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let typeKeyword;
|
|
118
|
+
|
|
119
|
+
// use 'string'
|
|
120
|
+
if (typeofMin === 'string' || typeofMax === 'string') {
|
|
121
|
+
typeKeyword = ts.SyntaxKind.StringKeyword;
|
|
122
|
+
}
|
|
123
|
+
// use 'number'
|
|
124
|
+
else if (typeofMin === 'number' || typeofMax === 'number') {
|
|
125
|
+
typeKeyword = ts.SyntaxKind.NumberKeyword;
|
|
126
|
+
}
|
|
127
|
+
// invalid type
|
|
128
|
+
else {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Invalid data type for min/max options. Must be string, number or undefined, but found { min: ${min} (${typeofMin}), max: ${max} (${typeofMax}) }`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
106
133
|
|
|
107
134
|
modifiers.push(
|
|
108
135
|
factory.createTypeReferenceNode(
|
|
109
136
|
factory.createIdentifier(withAttributeNamespace('SetMinMax')),
|
|
110
|
-
[toTypeLiteral(minMaxProperties)]
|
|
137
|
+
[toTypeLiteral(minMaxProperties), factory.createKeywordTypeNode(typeKeyword)]
|
|
111
138
|
)
|
|
112
139
|
);
|
|
113
140
|
}
|
|
@@ -124,8 +151,8 @@ const getAttributeModifiers = (attribute) => {
|
|
|
124
151
|
);
|
|
125
152
|
}
|
|
126
153
|
|
|
127
|
-
// Default
|
|
128
|
-
if (!_.isNil(attribute.default)) {
|
|
154
|
+
// Default (ignore if default is a function)
|
|
155
|
+
if (!_.isNil(attribute.default) && !_.isFunction(attribute.default)) {
|
|
129
156
|
const defaultLiteral = toTypeLiteral(attribute.default);
|
|
130
157
|
|
|
131
158
|
modifiers.push(
|
|
@@ -168,6 +195,4 @@ const attributeToPropertySignature = (schema, attributeName, attribute) => {
|
|
|
168
195
|
|
|
169
196
|
module.exports = attributeToPropertySignature;
|
|
170
197
|
|
|
171
|
-
module.exports
|
|
172
|
-
module.exports.getAttributeType = getAttributeType;
|
|
173
|
-
module.exports.getAttributeModifiers = getAttributeModifiers;
|
|
198
|
+
Object.assign(module.exports, { mappers, getAttributeModifiers, getAttributeType });
|
|
@@ -47,7 +47,7 @@ module.exports = {
|
|
|
47
47
|
decimal() {
|
|
48
48
|
return [withAttributeNamespace('Decimal')];
|
|
49
49
|
},
|
|
50
|
-
uid({ attribute
|
|
50
|
+
uid({ attribute }) {
|
|
51
51
|
const { targetField, options } = attribute;
|
|
52
52
|
|
|
53
53
|
// If there are no params to compute, then return the attribute type alone
|
|
@@ -58,18 +58,15 @@ module.exports = {
|
|
|
58
58
|
const params = [];
|
|
59
59
|
|
|
60
60
|
// If the targetField property is defined, then reference it,
|
|
61
|
-
// otherwise, put `undefined` keyword type
|
|
62
|
-
const
|
|
63
|
-
?
|
|
64
|
-
|
|
65
|
-
factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword),
|
|
66
|
-
]
|
|
67
|
-
: [factory.createStringLiteral(uid), factory.createStringLiteral(targetField)];
|
|
61
|
+
// otherwise, put `undefined` keyword type node as placeholder
|
|
62
|
+
const targetFieldParam = _.isUndefined(targetField)
|
|
63
|
+
? factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword)
|
|
64
|
+
: factory.createStringLiteral(targetField);
|
|
68
65
|
|
|
69
|
-
params.push(
|
|
66
|
+
params.push(targetFieldParam);
|
|
70
67
|
|
|
71
68
|
// If the options property is defined, transform it to
|
|
72
|
-
// a type
|
|
69
|
+
// a type literal node and add it to the params list
|
|
73
70
|
if (_.isObject(options)) {
|
|
74
71
|
params.push(toTypeLiteral(options));
|
|
75
72
|
}
|
|
@@ -87,28 +84,42 @@ module.exports = {
|
|
|
87
84
|
json() {
|
|
88
85
|
return [withAttributeNamespace('JSON')];
|
|
89
86
|
},
|
|
90
|
-
|
|
91
|
-
return [withAttributeNamespace('
|
|
87
|
+
blocks() {
|
|
88
|
+
return [withAttributeNamespace('Blocks')];
|
|
92
89
|
},
|
|
93
|
-
|
|
90
|
+
media({ attribute }) {
|
|
91
|
+
const { allowedTypes, multiple } = attribute;
|
|
92
|
+
|
|
93
|
+
const params = [];
|
|
94
|
+
|
|
95
|
+
const typesParam = allowedTypes
|
|
96
|
+
? factory.createUnionTypeNode(
|
|
97
|
+
allowedTypes.map((allowedType) => factory.createStringLiteral(allowedType))
|
|
98
|
+
)
|
|
99
|
+
: factory.createKeywordTypeNode(ts.SyntaxKind.UndefinedKeyword);
|
|
100
|
+
|
|
101
|
+
if (allowedTypes || multiple) {
|
|
102
|
+
params.push(typesParam);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (multiple) {
|
|
106
|
+
params.push(factory.createTrue());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return [withAttributeNamespace('Media'), params];
|
|
110
|
+
},
|
|
111
|
+
relation({ attribute }) {
|
|
94
112
|
const { relation, target } = attribute;
|
|
95
113
|
|
|
96
114
|
const isMorphRelation = relation.toLowerCase().includes('morph');
|
|
97
115
|
|
|
98
116
|
if (isMorphRelation) {
|
|
99
|
-
return [
|
|
100
|
-
withAttributeNamespace('Relation'),
|
|
101
|
-
[factory.createStringLiteral(uid, true), factory.createStringLiteral(relation, true)],
|
|
102
|
-
];
|
|
117
|
+
return [withAttributeNamespace('Relation'), [factory.createStringLiteral(relation, true)]];
|
|
103
118
|
}
|
|
104
119
|
|
|
105
120
|
return [
|
|
106
121
|
withAttributeNamespace('Relation'),
|
|
107
|
-
[
|
|
108
|
-
factory.createStringLiteral(uid, true),
|
|
109
|
-
factory.createStringLiteral(relation, true),
|
|
110
|
-
factory.createStringLiteral(target, true),
|
|
111
|
-
],
|
|
122
|
+
[factory.createStringLiteral(relation, true), factory.createStringLiteral(target, true)],
|
|
112
123
|
];
|
|
113
124
|
},
|
|
114
125
|
component({ attribute }) {
|
|
@@ -117,6 +128,8 @@ module.exports = {
|
|
|
117
128
|
|
|
118
129
|
if (attribute.repeatable) {
|
|
119
130
|
params.push(factory.createTrue());
|
|
131
|
+
} else {
|
|
132
|
+
params.push(factory.createFalse());
|
|
120
133
|
}
|
|
121
134
|
|
|
122
135
|
return [withAttributeNamespace('Component'), params];
|
|
@@ -22,9 +22,11 @@ const { addImport } = require('../imports');
|
|
|
22
22
|
const generateAttributePropertySignature = (schema) => {
|
|
23
23
|
const { attributes } = schema;
|
|
24
24
|
|
|
25
|
-
const properties = Object.entries(attributes)
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
const properties = Object.entries(attributes)
|
|
26
|
+
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
27
|
+
.map(([attributeName, attribute]) => {
|
|
28
|
+
return attributeToPropertySignature(schema, attributeName, attribute);
|
|
29
|
+
});
|
|
28
30
|
|
|
29
31
|
return factory.createPropertySignature(
|
|
30
32
|
undefined,
|
|
@@ -56,8 +58,8 @@ const generateSchemaDefinition = (schema) => {
|
|
|
56
58
|
const interfaceName = getSchemaInterfaceName(uid);
|
|
57
59
|
const parentType = getSchemaExtendsTypeName(schema);
|
|
58
60
|
|
|
59
|
-
// Make sure the
|
|
60
|
-
addImport(NAMESPACES.
|
|
61
|
+
// Make sure the Struct namespace is imported
|
|
62
|
+
addImport(NAMESPACES.Struct);
|
|
61
63
|
|
|
62
64
|
// Properties whose values can be mapped to a literal type expression
|
|
63
65
|
const literalPropertiesDefinitions = ['collectionName', 'info', 'options', 'pluginOptions']
|
|
@@ -18,8 +18,8 @@ const {
|
|
|
18
18
|
} = require('lodash/fp');
|
|
19
19
|
|
|
20
20
|
const NAMESPACES = {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
Struct: 'Struct',
|
|
22
|
+
Schema: 'Schema',
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -50,7 +50,7 @@ const getSchemaModelType = (schema) => {
|
|
|
50
50
|
* Get the parent type name to extend based on the schema's nature
|
|
51
51
|
*
|
|
52
52
|
* @param {object} schema
|
|
53
|
-
* @returns {string}
|
|
53
|
+
* @returns {string|null}
|
|
54
54
|
*/
|
|
55
55
|
const getSchemaExtendsTypeName = (schema) => {
|
|
56
56
|
const base = getSchemaModelType(schema);
|
|
@@ -59,7 +59,7 @@ const getSchemaExtendsTypeName = (schema) => {
|
|
|
59
59
|
return null;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
return `${NAMESPACES.
|
|
62
|
+
return `${NAMESPACES.Struct}.${upperFirst(base)}Schema`;
|
|
63
63
|
};
|
|
64
64
|
|
|
65
65
|
/**
|
|
@@ -92,7 +92,12 @@ const toTypeLiteral = (data) => {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
if (isNumber(data)) {
|
|
95
|
-
return
|
|
95
|
+
return data < 0
|
|
96
|
+
? factory.createPrefixUnaryExpression(
|
|
97
|
+
ts.SyntaxKind.MinusToken,
|
|
98
|
+
factory.createNumericLiteral(-data)
|
|
99
|
+
)
|
|
100
|
+
: factory.createNumericLiteral(data);
|
|
96
101
|
}
|
|
97
102
|
|
|
98
103
|
if (isBoolean(data)) {
|
|
@@ -111,7 +116,7 @@ const toTypeLiteral = (data) => {
|
|
|
111
116
|
throw new Error(`Cannot convert to object literal. Unknown type "${typeof data}"`);
|
|
112
117
|
}
|
|
113
118
|
|
|
114
|
-
const entries = Object.entries(data);
|
|
119
|
+
const entries = Object.entries(data).sort((a, b) => a[0].localeCompare(b[0]));
|
|
115
120
|
|
|
116
121
|
const props = entries.reduce((acc, [key, value]) => {
|
|
117
122
|
// Handle keys such as content-type-builder & co.
|
|
@@ -145,12 +150,12 @@ const getDefinitionAttributesCount = (definition) => {
|
|
|
145
150
|
};
|
|
146
151
|
|
|
147
152
|
/**
|
|
148
|
-
* Add the
|
|
153
|
+
* Add the Schema.Attribute namespace before the typename
|
|
149
154
|
*
|
|
150
155
|
* @param {string} typeName
|
|
151
156
|
* @returns {string}
|
|
152
157
|
*/
|
|
153
|
-
const withAttributeNamespace = (typeName) => `${NAMESPACES.
|
|
158
|
+
const withAttributeNamespace = (typeName) => `${NAMESPACES.Schema}.Attribute.${typeName}`;
|
|
154
159
|
|
|
155
160
|
/**
|
|
156
161
|
* Add the schema namespace before the typename
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { factory } = require('typescript');
|
|
4
|
+
const { pipe, values, sortBy, map } = require('lodash/fp');
|
|
4
5
|
|
|
5
6
|
const { models } = require('../common');
|
|
6
7
|
const { emitDefinitions, format, generateSharedExtensionDefinition } = require('../utils');
|
|
7
8
|
|
|
9
|
+
const NO_COMPONENT_PLACEHOLDER_COMMENT = `/*
|
|
10
|
+
* The app doesn't have any components yet.
|
|
11
|
+
*/
|
|
12
|
+
`;
|
|
13
|
+
|
|
8
14
|
/**
|
|
9
15
|
* Generate type definitions for Strapi Components
|
|
10
16
|
*
|
|
@@ -18,10 +24,20 @@ const generateComponentsDefinitions = async (options = {}) => {
|
|
|
18
24
|
|
|
19
25
|
const { components } = strapi;
|
|
20
26
|
|
|
21
|
-
const componentsDefinitions =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
const componentsDefinitions = pipe(
|
|
28
|
+
values,
|
|
29
|
+
sortBy('uid'),
|
|
30
|
+
map((component) => ({
|
|
31
|
+
uid: component.uid,
|
|
32
|
+
definition: models.schema.generateSchemaDefinition(component),
|
|
33
|
+
}))
|
|
34
|
+
)(components);
|
|
35
|
+
|
|
36
|
+
options.logger.debug(`Found ${componentsDefinitions.length} components.`);
|
|
37
|
+
|
|
38
|
+
if (componentsDefinitions.length === 0) {
|
|
39
|
+
return { output: NO_COMPONENT_PLACEHOLDER_COMMENT, stats: {} };
|
|
40
|
+
}
|
|
25
41
|
|
|
26
42
|
const formattedSchemasDefinitions = componentsDefinitions.reduce((acc, def) => {
|
|
27
43
|
acc.push(
|
|
@@ -46,7 +62,7 @@ const generateComponentsDefinitions = async (options = {}) => {
|
|
|
46
62
|
...formattedSchemasDefinitions,
|
|
47
63
|
|
|
48
64
|
// Global
|
|
49
|
-
generateSharedExtensionDefinition('
|
|
65
|
+
generateSharedExtensionDefinition('ComponentSchemas', componentsDefinitions),
|
|
50
66
|
];
|
|
51
67
|
|
|
52
68
|
const output = emitDefinitions(allDefinitions);
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { factory } = require('typescript');
|
|
4
|
+
const { values, pipe, map, sortBy } = require('lodash/fp');
|
|
4
5
|
|
|
5
6
|
const { models } = require('../common');
|
|
6
7
|
const { emitDefinitions, format, generateSharedExtensionDefinition } = require('../utils');
|
|
7
8
|
|
|
9
|
+
const NO_CONTENT_TYPE_PLACEHOLDER_COMMENT = `/*
|
|
10
|
+
* The app doesn't have any content-types yet.
|
|
11
|
+
*/
|
|
12
|
+
`;
|
|
13
|
+
|
|
8
14
|
/**
|
|
9
15
|
* Generate type definitions for Strapi Content-Types
|
|
10
16
|
*
|
|
@@ -18,10 +24,20 @@ const generateContentTypesDefinitions = async (options = {}) => {
|
|
|
18
24
|
|
|
19
25
|
const { contentTypes } = strapi;
|
|
20
26
|
|
|
21
|
-
const contentTypesDefinitions =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
27
|
+
const contentTypesDefinitions = pipe(
|
|
28
|
+
values,
|
|
29
|
+
sortBy('uid'),
|
|
30
|
+
map((contentType) => ({
|
|
31
|
+
uid: contentType.uid,
|
|
32
|
+
definition: models.schema.generateSchemaDefinition(contentType),
|
|
33
|
+
}))
|
|
34
|
+
)(contentTypes);
|
|
35
|
+
|
|
36
|
+
options.logger.debug(`Found ${contentTypesDefinitions.length} content-types.`);
|
|
37
|
+
|
|
38
|
+
if (contentTypesDefinitions.length === 0) {
|
|
39
|
+
return { output: NO_CONTENT_TYPE_PLACEHOLDER_COMMENT, stats: {} };
|
|
40
|
+
}
|
|
25
41
|
|
|
26
42
|
const formattedSchemasDefinitions = contentTypesDefinitions.reduce((acc, def) => {
|
|
27
43
|
acc.push(
|
|
@@ -46,7 +62,7 @@ const generateContentTypesDefinitions = async (options = {}) => {
|
|
|
46
62
|
...formattedSchemasDefinitions,
|
|
47
63
|
|
|
48
64
|
// Global
|
|
49
|
-
generateSharedExtensionDefinition('
|
|
65
|
+
generateSharedExtensionDefinition('ContentTypeSchemas', contentTypesDefinitions),
|
|
50
66
|
];
|
|
51
67
|
|
|
52
68
|
const output = emitDefinitions(allDefinitions);
|
package/lib/generators/index.js
CHANGED
|
@@ -100,7 +100,7 @@ const generate = async (config = {}) => {
|
|
|
100
100
|
|
|
101
101
|
try {
|
|
102
102
|
const outPath = await saveDefinitionToFileSystem(registryPwd, filename, report.output);
|
|
103
|
-
const relativeOutPath = path.relative(
|
|
103
|
+
const relativeOutPath = path.relative(process.cwd(), outPath);
|
|
104
104
|
|
|
105
105
|
artifactFsTimer.end();
|
|
106
106
|
|
package/lib/generators/utils.js
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const assert = require('assert');
|
|
3
5
|
const ts = require('typescript');
|
|
4
|
-
const prettier = require('prettier');
|
|
5
6
|
const fse = require('fs-extra');
|
|
6
|
-
const path = require('path');
|
|
7
7
|
const chalk = require('chalk');
|
|
8
|
-
const assert = require('assert');
|
|
9
8
|
|
|
10
9
|
const { factory } = ts;
|
|
11
10
|
|
|
11
|
+
const MODULE_DECLARATION = '@strapi/strapi';
|
|
12
|
+
const PUBLIC_NAMESPACE = 'Public';
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* Aggregate the given TypeScript nodes into a single string
|
|
14
16
|
*
|
|
@@ -58,6 +60,9 @@ const saveDefinitionToFileSystem = async (dir, file, content) => {
|
|
|
58
60
|
* @returns {Promise<string>}
|
|
59
61
|
*/
|
|
60
62
|
const format = async (content) => {
|
|
63
|
+
// eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
64
|
+
const prettier = await import('prettier'); // ESM-only
|
|
65
|
+
|
|
61
66
|
const configFile = await prettier.resolveConfigFile();
|
|
62
67
|
const config = configFile
|
|
63
68
|
? await prettier.resolveConfig(configFile)
|
|
@@ -92,11 +97,11 @@ const generateSharedExtensionDefinition = (registry, definitions) => {
|
|
|
92
97
|
|
|
93
98
|
return factory.createModuleDeclaration(
|
|
94
99
|
[factory.createModifier(ts.SyntaxKind.DeclareKeyword)],
|
|
95
|
-
factory.createStringLiteral(
|
|
100
|
+
factory.createStringLiteral(MODULE_DECLARATION, true),
|
|
96
101
|
factory.createModuleBlock([
|
|
97
102
|
factory.createModuleDeclaration(
|
|
98
103
|
[factory.createModifier(ts.SyntaxKind.ExportKeyword)],
|
|
99
|
-
factory.createIdentifier(
|
|
104
|
+
factory.createIdentifier(PUBLIC_NAMESPACE),
|
|
100
105
|
factory.createModuleBlock(
|
|
101
106
|
properties.length > 0
|
|
102
107
|
? [
|
package/lib/index.js
CHANGED
|
@@ -2,15 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const compile = require('./compile');
|
|
4
4
|
const compilers = require('./compilers');
|
|
5
|
-
const admin = require('./admin');
|
|
6
5
|
const utils = require('./utils');
|
|
7
6
|
const generators = require('./generators');
|
|
8
7
|
|
|
9
8
|
module.exports = {
|
|
10
9
|
compile,
|
|
11
10
|
compilers,
|
|
12
|
-
admin,
|
|
13
11
|
generators,
|
|
14
|
-
|
|
15
12
|
...utils,
|
|
16
13
|
};
|
package/lib/utils/index.js
CHANGED
|
@@ -7,6 +7,7 @@ const reportDiagnostics = require('./report-diagnostics');
|
|
|
7
7
|
const resolveConfigOptions = require('./resolve-config-options');
|
|
8
8
|
const formatHost = require('./format-host');
|
|
9
9
|
const resolveOutDir = require('./resolve-outdir');
|
|
10
|
+
const resolveOutDirSync = require('./resolve-outdir-sync');
|
|
10
11
|
|
|
11
12
|
module.exports = {
|
|
12
13
|
isUsingTypeScript,
|
|
@@ -16,4 +17,5 @@ module.exports = {
|
|
|
16
17
|
resolveConfigOptions,
|
|
17
18
|
formatHost,
|
|
18
19
|
resolveOutDir,
|
|
20
|
+
resolveOutDirSync,
|
|
19
21
|
};
|