@strapi/strapi 4.0.0-next.6 → 4.0.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 (166) hide show
  1. package/README.md +12 -12
  2. package/bin/strapi.js +41 -60
  3. package/lib/Strapi.js +234 -114
  4. package/lib/commands/build.js +16 -6
  5. package/lib/commands/console.js +1 -1
  6. package/lib/commands/content-types/list.js +22 -0
  7. package/lib/commands/develop.js +17 -18
  8. package/lib/commands/generate-template.js +4 -5
  9. package/lib/commands/hooks/list.js +22 -0
  10. package/lib/commands/middlewares/list.js +22 -0
  11. package/lib/commands/new.js +3 -1
  12. package/lib/commands/policies/list.js +22 -0
  13. package/lib/commands/routes/list.js +28 -0
  14. package/lib/commands/services/list.js +22 -0
  15. package/lib/commands/watchAdmin.js +18 -8
  16. package/lib/container.js +6 -6
  17. package/lib/core/app-configuration/config-loader.js +1 -37
  18. package/lib/core/app-configuration/index.js +6 -46
  19. package/lib/core/app-configuration/load-config-file.js +43 -0
  20. package/lib/core/bootstrap.js +5 -117
  21. package/lib/core/domain/component/index.js +24 -0
  22. package/lib/core/domain/component/validator.js +29 -0
  23. package/lib/core/domain/content-type/index.js +140 -0
  24. package/lib/core/domain/content-type/validator.js +64 -0
  25. package/lib/core/domain/module/index.js +108 -0
  26. package/lib/core/domain/module/validation.js +33 -0
  27. package/lib/core/loaders/admin.js +16 -0
  28. package/lib/core/loaders/apis.js +159 -0
  29. package/lib/core/{load-components.js → loaders/components.js} +5 -7
  30. package/lib/core/loaders/index.js +11 -0
  31. package/lib/core/loaders/middlewares.js +36 -0
  32. package/lib/core/loaders/plugins/get-enabled-plugins.js +116 -0
  33. package/lib/core/loaders/plugins/index.js +123 -0
  34. package/lib/core/loaders/policies.js +28 -0
  35. package/lib/core/loaders/src-index.js +39 -0
  36. package/lib/core/registries/apis.js +29 -0
  37. package/lib/core/{app-configuration/config-provider.js → registries/config.js} +4 -11
  38. package/lib/core/registries/content-types.js +97 -0
  39. package/lib/core/registries/controllers.d.ts +7 -0
  40. package/lib/core/registries/controllers.js +114 -0
  41. package/lib/core/registries/hooks.d.ts +20 -0
  42. package/lib/core/registries/hooks.js +87 -0
  43. package/lib/core/registries/middlewares.d.ts +5 -0
  44. package/lib/core/registries/middlewares.js +89 -0
  45. package/lib/core/registries/modules.js +44 -0
  46. package/lib/core/registries/plugins.js +28 -0
  47. package/lib/core/registries/policies.d.ts +9 -0
  48. package/lib/core/registries/policies.js +89 -0
  49. package/lib/core/registries/services.d.ts +7 -0
  50. package/lib/core/registries/services.js +114 -0
  51. package/lib/core/utils.js +35 -0
  52. package/lib/core-api/controller/collection-type.js +45 -26
  53. package/lib/core-api/controller/index.d.ts +25 -0
  54. package/lib/core-api/controller/index.js +33 -11
  55. package/lib/core-api/controller/single-type.js +29 -15
  56. package/lib/core-api/controller/transform.js +62 -6
  57. package/lib/core-api/routes/index.js +71 -0
  58. package/lib/core-api/service/collection-type.js +43 -21
  59. package/lib/core-api/service/index.d.ts +21 -0
  60. package/lib/core-api/service/index.js +8 -67
  61. package/lib/core-api/service/pagination.js +130 -0
  62. package/lib/core-api/service/single-type.js +17 -19
  63. package/lib/factories.d.ts +48 -0
  64. package/lib/factories.js +84 -0
  65. package/lib/index.d.ts +10 -31
  66. package/lib/index.js +5 -1
  67. package/lib/middlewares/body.js +33 -0
  68. package/lib/middlewares/compression.js +8 -0
  69. package/lib/middlewares/cors.js +58 -0
  70. package/lib/middlewares/errors.js +40 -0
  71. package/lib/middlewares/favicon.js +19 -0
  72. package/lib/middlewares/index.d.ts +5 -0
  73. package/lib/middlewares/index.js +30 -117
  74. package/lib/middlewares/ip.js +8 -0
  75. package/lib/middlewares/logger.js +27 -0
  76. package/lib/middlewares/powered-by.js +20 -0
  77. package/lib/middlewares/public/index.js +98 -73
  78. package/lib/middlewares/query.js +46 -0
  79. package/lib/middlewares/response-time.js +15 -0
  80. package/lib/middlewares/responses.js +19 -0
  81. package/lib/middlewares/security.js +51 -0
  82. package/lib/middlewares/session/index.js +6 -6
  83. package/lib/migrations/draft-publish.js +57 -0
  84. package/lib/services/auth/index.js +87 -0
  85. package/lib/services/core-store.js +64 -51
  86. package/lib/services/cron.js +54 -0
  87. package/lib/services/entity-service/attributes/index.js +31 -0
  88. package/lib/services/entity-service/attributes/transforms.js +20 -0
  89. package/lib/services/entity-service/components.js +39 -15
  90. package/lib/services/entity-service/index.d.ts +91 -0
  91. package/lib/services/entity-service/index.js +120 -59
  92. package/lib/services/entity-service/params.js +52 -94
  93. package/lib/services/entity-validator/index.js +76 -43
  94. package/lib/services/entity-validator/validators.js +129 -43
  95. package/lib/services/errors.js +77 -0
  96. package/lib/{core → services}/fs.js +10 -2
  97. package/lib/services/metrics/index.js +41 -38
  98. package/lib/services/metrics/sender.js +2 -2
  99. package/lib/services/server/admin-api.js +14 -0
  100. package/lib/services/server/api.js +36 -0
  101. package/lib/services/server/compose-endpoint.js +141 -0
  102. package/lib/services/server/content-api.js +16 -0
  103. package/lib/{server.js → services/server/http-server.js} +0 -0
  104. package/lib/services/server/index.js +127 -0
  105. package/lib/services/server/koa.js +64 -0
  106. package/lib/services/server/middleware.js +122 -0
  107. package/lib/services/server/policy.js +32 -0
  108. package/lib/services/server/register-middlewares.js +110 -0
  109. package/lib/services/server/register-routes.js +106 -0
  110. package/lib/services/server/routing.js +120 -0
  111. package/lib/services/utils/upload-files.js +1 -1
  112. package/lib/services/webhook-runner.js +1 -1
  113. package/lib/utils/ee.js +3 -3
  114. package/lib/utils/get-dirs.js +17 -0
  115. package/lib/utils/index.js +2 -0
  116. package/lib/utils/is-initialized.js +1 -1
  117. package/lib/utils/run-checks.js +0 -15
  118. package/lib/utils/signals.js +24 -0
  119. package/lib/utils/startup-logger.js +2 -2
  120. package/lib/utils/update-notifier/index.js +3 -2
  121. package/package.json +93 -96
  122. package/lib/commands/generate.js +0 -76
  123. package/lib/core/index.js +0 -17
  124. package/lib/core/load-apis.js +0 -20
  125. package/lib/core/load-extensions.js +0 -71
  126. package/lib/core/load-functions.js +0 -21
  127. package/lib/core/load-middlewares.js +0 -130
  128. package/lib/core/load-modules.js +0 -55
  129. package/lib/core/load-plugins.js +0 -68
  130. package/lib/core/load-policies.js +0 -36
  131. package/lib/core/walk.js +0 -27
  132. package/lib/core-api/index.js +0 -39
  133. package/lib/load/check-reserved-filename.js +0 -10
  134. package/lib/load/load-config-files.js +0 -22
  135. package/lib/load/require-file-parse.js +0 -15
  136. package/lib/middlewares/boom/defaults.json +0 -5
  137. package/lib/middlewares/boom/index.js +0 -147
  138. package/lib/middlewares/cors/index.js +0 -66
  139. package/lib/middlewares/cron/defaults.json +0 -5
  140. package/lib/middlewares/cron/index.js +0 -43
  141. package/lib/middlewares/favicon/defaults.json +0 -7
  142. package/lib/middlewares/favicon/index.js +0 -32
  143. package/lib/middlewares/gzip/defaults.json +0 -6
  144. package/lib/middlewares/gzip/index.js +0 -19
  145. package/lib/middlewares/helmet/defaults.json +0 -18
  146. package/lib/middlewares/helmet/index.js +0 -9
  147. package/lib/middlewares/ip/defaults.json +0 -7
  148. package/lib/middlewares/ip/index.js +0 -25
  149. package/lib/middlewares/language/defaults.json +0 -9
  150. package/lib/middlewares/language/index.js +0 -40
  151. package/lib/middlewares/logger/defaults.json +0 -5
  152. package/lib/middlewares/logger/index.js +0 -37
  153. package/lib/middlewares/parser/defaults.json +0 -11
  154. package/lib/middlewares/parser/index.js +0 -71
  155. package/lib/middlewares/poweredBy/defaults.json +0 -5
  156. package/lib/middlewares/poweredBy/index.js +0 -16
  157. package/lib/middlewares/public/defaults.json +0 -8
  158. package/lib/middlewares/responseTime/defaults.json +0 -5
  159. package/lib/middlewares/responseTime/index.js +0 -25
  160. package/lib/middlewares/responses/defaults.json +0 -5
  161. package/lib/middlewares/responses/index.js +0 -18
  162. package/lib/middlewares/router/defaults.json +0 -7
  163. package/lib/middlewares/router/index.js +0 -64
  164. package/lib/middlewares/router/utils/composeEndpoint.js +0 -25
  165. package/lib/middlewares/router/utils/routerChecker.js +0 -92
  166. package/lib/utils/get-prefixed-dependencies.js +0 -7
@@ -0,0 +1,140 @@
1
+ 'use strict';
2
+
3
+ const { cloneDeep } = require('lodash/fp');
4
+ const _ = require('lodash');
5
+ const { hasDraftAndPublish, getPrivateAttributes } = require('@strapi/utils').contentTypes;
6
+ const {
7
+ CREATED_AT_ATTRIBUTE,
8
+ UPDATED_AT_ATTRIBUTE,
9
+ PUBLISHED_AT_ATTRIBUTE,
10
+ CREATED_BY_ATTRIBUTE,
11
+ UPDATED_BY_ATTRIBUTE,
12
+ } = require('@strapi/utils').contentTypes.constants;
13
+ const { validateContentTypeDefinition } = require('./validator');
14
+
15
+ const createContentType = (uid, definition) => {
16
+ try {
17
+ validateContentTypeDefinition(definition);
18
+ } catch (e) {
19
+ throw new Error(`Content Type Definition is invalid for ${uid}'.\n${e.errors}`);
20
+ }
21
+
22
+ const { schema, actions, lifecycles } = cloneDeep(definition);
23
+
24
+ // general info
25
+ Object.assign(schema, {
26
+ kind: schema.kind || 'collectionType',
27
+ __schema__: pickSchema(definition.schema),
28
+ modelType: 'contentType',
29
+ modelName: definition.schema.info.singularName,
30
+ connection: 'default',
31
+ });
32
+
33
+ if (uid.startsWith('api::')) {
34
+ Object.assign(schema, {
35
+ uid,
36
+ apiName: uid.split('::')[1].split('.')[0],
37
+ collectionName: schema.collectionName || schema.info.singularName,
38
+ globalId: getGlobalId(schema, schema.info.singularName),
39
+ });
40
+ } else if (uid.startsWith('plugin::')) {
41
+ const pluginName = uid.split('::')[1].split('.')[0];
42
+ Object.assign(schema, {
43
+ uid,
44
+ plugin: pluginName, // TODO: to be set in load-plugins.js
45
+ collectionName:
46
+ schema.collectionName || `${pluginName}_${schema.info.singularName}`.toLowerCase(),
47
+ globalId: getGlobalId(schema, schema.info.singularName, pluginName),
48
+ });
49
+ } else if (uid.startsWith('admin::')) {
50
+ Object.assign(schema, {
51
+ uid,
52
+ plugin: 'admin',
53
+ globalId: getGlobalId(schema, schema.info.singularName, 'admin'),
54
+ });
55
+ } else {
56
+ throw new Error(
57
+ `Incorrect Content Type UID "${uid}". The UID should start with api::, plugin:: or strapi::.`
58
+ );
59
+ }
60
+
61
+ Object.defineProperty(schema, 'privateAttributes', {
62
+ get() {
63
+ return getPrivateAttributes(schema);
64
+ },
65
+ });
66
+
67
+ // attributes
68
+ Object.assign(schema.attributes, {
69
+ [CREATED_AT_ATTRIBUTE]: {
70
+ type: 'datetime',
71
+ },
72
+ // TODO: handle on edit set to new date
73
+ [UPDATED_AT_ATTRIBUTE]: {
74
+ type: 'datetime',
75
+ },
76
+ });
77
+
78
+ if (hasDraftAndPublish(schema)) {
79
+ schema.attributes[PUBLISHED_AT_ATTRIBUTE] = {
80
+ type: 'datetime',
81
+ configurable: false,
82
+ writable: true,
83
+ visible: false,
84
+ };
85
+ }
86
+
87
+ const isPrivate = !_.get(schema, 'options.populateCreatorFields', false);
88
+
89
+ schema.attributes[CREATED_BY_ATTRIBUTE] = {
90
+ type: 'relation',
91
+ relation: 'oneToOne',
92
+ target: 'admin::user',
93
+ configurable: false,
94
+ writable: false,
95
+ visible: false,
96
+ useJoinTable: false,
97
+ private: isPrivate,
98
+ };
99
+
100
+ schema.attributes[UPDATED_BY_ATTRIBUTE] = {
101
+ type: 'relation',
102
+ relation: 'oneToOne',
103
+ target: 'admin::user',
104
+ configurable: false,
105
+ writable: false,
106
+ visible: false,
107
+ useJoinTable: false,
108
+ private: isPrivate,
109
+ };
110
+
111
+ Object.assign(schema, { actions, lifecycles });
112
+
113
+ return schema;
114
+ };
115
+
116
+ const getGlobalId = (model, modelName, prefix) => {
117
+ let globalId = prefix ? `${prefix}-${modelName}` : modelName;
118
+
119
+ return model.globalId || _.upperFirst(_.camelCase(globalId));
120
+ };
121
+
122
+ const pickSchema = model => {
123
+ const schema = _.cloneDeep(
124
+ _.pick(model, [
125
+ 'connection',
126
+ 'collectionName',
127
+ 'info',
128
+ 'options',
129
+ 'pluginOptions',
130
+ 'attributes',
131
+ ])
132
+ );
133
+
134
+ schema.kind = model.kind || 'collectionType';
135
+ return schema;
136
+ };
137
+
138
+ module.exports = {
139
+ createContentType,
140
+ };
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ const { keyBy, mapValues } = require('lodash');
4
+ const { yup } = require('@strapi/utils');
5
+
6
+ const LIFECYCLES = [
7
+ 'beforeCreate',
8
+ 'afterCreate',
9
+ 'beforeFindOne',
10
+ 'afterFindOne',
11
+ 'beforeFindMany',
12
+ 'afterFindMany',
13
+ 'beforeCount',
14
+ 'afterCount',
15
+ 'beforeCreateMany',
16
+ 'afterCreateMany',
17
+ 'beforeUpdate',
18
+ 'afterUpdate',
19
+ 'beforeUpdateMany',
20
+ 'afterUpdateMany',
21
+ 'beforeDelete',
22
+ 'afterDelete',
23
+ 'beforeDeleteMany',
24
+ 'afterDeleteMany',
25
+ ];
26
+
27
+ const lifecyclesShape = mapValues(keyBy(LIFECYCLES), () =>
28
+ yup
29
+ .mixed()
30
+ .nullable()
31
+ .isFunction()
32
+ );
33
+
34
+ const contentTypeSchemaValidator = yup.object().shape({
35
+ schema: yup.object().shape({
36
+ info: yup
37
+ .object()
38
+ .shape({
39
+ displayName: yup.string().required(),
40
+ singularName: yup
41
+ .string()
42
+ .isKebabCase()
43
+ .required(),
44
+ pluralName: yup
45
+ .string()
46
+ .isKebabCase()
47
+ .required(),
48
+ })
49
+ .required(),
50
+ }),
51
+ actions: yup.object().onlyContainsFunctions(),
52
+ lifecycles: yup
53
+ .object()
54
+ .shape(lifecyclesShape)
55
+ .noUnknown(),
56
+ });
57
+
58
+ const validateContentTypeDefinition = data => {
59
+ return contentTypeSchemaValidator.validateSync(data, { strict: true, abortEarly: false });
60
+ };
61
+
62
+ module.exports = {
63
+ validateContentTypeDefinition,
64
+ };
@@ -0,0 +1,108 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+ const { removeNamespace } = require('../../utils');
5
+ const { validateModule } = require('./validation');
6
+
7
+ const uidToPath = uid => uid.replace('::', '.');
8
+
9
+ // Removes the namespace from a map with keys prefixed with a namespace
10
+ const removeNamespacedKeys = (map, namespace) => {
11
+ return _.mapKeys(map, (value, key) => removeNamespace(key, namespace));
12
+ };
13
+
14
+ const defaultModule = {
15
+ config: {},
16
+ routes: [],
17
+ controllers: {},
18
+ services: {},
19
+ contentTypes: {},
20
+ policies: {},
21
+ middlewares: {},
22
+ };
23
+
24
+ const createModule = (namespace, rawModule, strapi) => {
25
+ _.defaults(rawModule, defaultModule);
26
+
27
+ try {
28
+ validateModule(rawModule);
29
+ } catch (e) {
30
+ throw new Error(`strapi-server.js is invalid for '${namespace}'.\n${e.errors.join('\n')}`);
31
+ }
32
+
33
+ const called = {};
34
+ return {
35
+ async bootstrap() {
36
+ if (called.bootstrap) {
37
+ throw new Error(`Bootstrap for ${namespace} has already been called`);
38
+ }
39
+ called.bootstrap = true;
40
+ await (rawModule.bootstrap && rawModule.bootstrap({ strapi }));
41
+ },
42
+ async register() {
43
+ if (called.register) {
44
+ throw new Error(`Register for ${namespace} has already been called`);
45
+ }
46
+ called.register = true;
47
+ await (rawModule.register && rawModule.register({ strapi }));
48
+ },
49
+ async destroy() {
50
+ if (called.destroy) {
51
+ throw new Error(`Destroy for ${namespace} has already been called`);
52
+ }
53
+ called.destroy = true;
54
+ await (rawModule.destroy && rawModule.destroy({ strapi }));
55
+ },
56
+ load() {
57
+ strapi.container.get('content-types').add(namespace, rawModule.contentTypes);
58
+ strapi.container.get('services').add(namespace, rawModule.services);
59
+ strapi.container.get('policies').add(namespace, rawModule.policies);
60
+ strapi.container.get('middlewares').add(namespace, rawModule.middlewares);
61
+ strapi.container.get('controllers').add(namespace, rawModule.controllers);
62
+ strapi.container.get('config').set(uidToPath(namespace), rawModule.config);
63
+ },
64
+ get routes() {
65
+ return rawModule.routes;
66
+ },
67
+ config(path, defaultValue) {
68
+ return strapi.container.get('config').get(`${uidToPath(namespace)}.${path}`, defaultValue);
69
+ },
70
+ contentType(ctName) {
71
+ return strapi.container.get('content-types').get(`${namespace}.${ctName}`);
72
+ },
73
+ get contentTypes() {
74
+ const contentTypes = strapi.container.get('content-types').getAll(namespace);
75
+ return removeNamespacedKeys(contentTypes, namespace);
76
+ },
77
+ service(serviceName) {
78
+ return strapi.container.get('services').get(`${namespace}.${serviceName}`);
79
+ },
80
+ get services() {
81
+ const services = strapi.container.get('services').getAll(namespace);
82
+ return removeNamespacedKeys(services, namespace);
83
+ },
84
+ policy(policyName) {
85
+ return strapi.container.get('policies').get(`${namespace}.${policyName}`);
86
+ },
87
+ get policies() {
88
+ const policies = strapi.container.get('policies').getAll(namespace);
89
+ return removeNamespacedKeys(policies, namespace);
90
+ },
91
+ middleware(middlewareName) {
92
+ return strapi.container.get('middlewares').get(`${namespace}.${middlewareName}`);
93
+ },
94
+ get middlewares() {
95
+ const middlewares = strapi.container.get('middlewares').getAll(namespace);
96
+ return removeNamespacedKeys(middlewares, namespace);
97
+ },
98
+ controller(controllerName) {
99
+ return strapi.container.get('controllers').get(`${namespace}.${controllerName}`);
100
+ },
101
+ get controllers() {
102
+ const controllers = strapi.container.get('controllers').getAll(namespace);
103
+ return removeNamespacedKeys(controllers, namespace);
104
+ },
105
+ };
106
+ };
107
+
108
+ module.exports = { createModule };
@@ -0,0 +1,33 @@
1
+ 'use strict';
2
+
3
+ const { yup } = require('@strapi/utils');
4
+
5
+ const strapiServerSchema = yup
6
+ .object()
7
+ .shape({
8
+ bootstrap: yup.mixed().isFunction(),
9
+ destroy: yup.mixed().isFunction(),
10
+ register: yup.mixed().isFunction(),
11
+ config: yup.object(),
12
+ routes: yup.lazy(value => {
13
+ if (Array.isArray(value)) {
14
+ return yup.array();
15
+ } else {
16
+ return yup.object();
17
+ }
18
+ }),
19
+ controllers: yup.object(),
20
+ services: yup.object(),
21
+ policies: yup.object(),
22
+ middlewares: yup.object(),
23
+ contentTypes: yup.object(),
24
+ })
25
+ .noUnknown();
26
+
27
+ const validateModule = data => {
28
+ return strapiServerSchema.validateSync(data, { strict: true, abortEarly: false });
29
+ };
30
+
31
+ module.exports = {
32
+ validateModule,
33
+ };
@@ -0,0 +1,16 @@
1
+ 'use strict';
2
+
3
+ const _ = require('lodash');
4
+
5
+ module.exports = strapi => {
6
+ strapi.admin = require('@strapi/admin/strapi-server');
7
+
8
+ strapi.container.get('services').add(`admin::`, strapi.admin.services);
9
+ strapi.container.get('controllers').add(`admin::`, strapi.admin.controllers);
10
+ strapi.container.get('content-types').add(`admin::`, strapi.admin.contentTypes);
11
+ strapi.container.get('policies').add(`admin::`, strapi.admin.policies);
12
+ strapi.container.get('middlewares').add(`admin::`, strapi.admin.middlewares);
13
+
14
+ const userAdminConfig = strapi.config.get('admin');
15
+ strapi.container.get('config').set('admin', _.merge(strapi.admin.config, userAdminConfig));
16
+ };
@@ -0,0 +1,159 @@
1
+ 'use strict';
2
+
3
+ const { join, extname, basename } = require('path');
4
+ const { existsSync } = require('fs-extra');
5
+ const _ = require('lodash');
6
+ const fse = require('fs-extra');
7
+ const { isKebabCase } = require('@strapi/utils');
8
+
9
+ // to handle names with numbers in it we first check if it is already in kebabCase
10
+ const normalizeName = name => (isKebabCase(name) ? name : _.kebabCase(name));
11
+
12
+ const DEFAULT_CONTENT_TYPE = {
13
+ schema: {},
14
+ actions: {},
15
+ lifecycles: {},
16
+ };
17
+
18
+ module.exports = async strapi => {
19
+ if (!existsSync(strapi.dirs.api)) {
20
+ throw new Error('Missing api folder. Please create one at `./src/api`');
21
+ }
22
+
23
+ const apisFDs = await fse.readdir(strapi.dirs.api, { withFileTypes: true });
24
+ const apis = {};
25
+
26
+ // only load folders
27
+ for (const apiFD of apisFDs) {
28
+ if (apiFD.isDirectory()) {
29
+ const apiName = normalizeName(apiFD.name);
30
+ const api = await loadAPI(join(strapi.dirs.api, apiFD.name));
31
+
32
+ apis[apiName] = api;
33
+ }
34
+ }
35
+
36
+ validateContentTypesUnicity(apis);
37
+
38
+ for (const apiName in apis) {
39
+ strapi.container.get('apis').add(apiName, apis[apiName]);
40
+ }
41
+ };
42
+
43
+ const validateContentTypesUnicity = apis => {
44
+ const allApisSchemas = Object.values(apis).flatMap(api => Object.values(api.contentTypes));
45
+
46
+ const names = [];
47
+ allApisSchemas.forEach(({ schema }) => {
48
+ if (schema.info.singularName) {
49
+ const singularName = _.kebabCase(schema.info.singularName);
50
+ if (names.includes(singularName)) {
51
+ throw new Error(`The singular name "${schema.info.singularName}" should be unique`);
52
+ }
53
+ names.push(singularName);
54
+ }
55
+
56
+ if (schema.info.pluralName) {
57
+ const pluralName = _.kebabCase(schema.info.pluralName);
58
+ if (names.includes(pluralName)) {
59
+ throw new Error(`The plural name "${schema.info.pluralName}" should be unique`);
60
+ }
61
+ names.push(pluralName);
62
+ }
63
+ });
64
+ };
65
+
66
+ const loadAPI = async dir => {
67
+ const [
68
+ index,
69
+ config,
70
+ routes,
71
+ controllers,
72
+ services,
73
+ policies,
74
+ middlewares,
75
+ contentTypes,
76
+ ] = await Promise.all([
77
+ loadIndex(dir),
78
+ loadDir(join(dir, 'config')),
79
+ loadDir(join(dir, 'routes')),
80
+ loadDir(join(dir, 'controllers')),
81
+ loadDir(join(dir, 'services')),
82
+ loadDir(join(dir, 'policies')),
83
+ loadDir(join(dir, 'middlewares')),
84
+ loadContentTypes(join(dir, 'content-types')),
85
+ ]);
86
+
87
+ return {
88
+ ...(index || {}),
89
+ config: config || {},
90
+ routes: routes || [],
91
+ controllers: controllers || {},
92
+ services: services || {},
93
+ policies: policies || {},
94
+ middlewares: middlewares || {},
95
+ contentTypes: contentTypes || {},
96
+ };
97
+ };
98
+
99
+ const loadIndex = async dir => {
100
+ if (await fse.pathExists(join(dir, 'index.js'))) {
101
+ return loadFile(join(dir, 'index.js'));
102
+ }
103
+ };
104
+
105
+ const loadContentTypes = async dir => {
106
+ if (!(await fse.pathExists(dir))) {
107
+ return;
108
+ }
109
+
110
+ const fds = await fse.readdir(dir, { withFileTypes: true });
111
+ const contentTypes = {};
112
+
113
+ // only load folders
114
+ for (const fd of fds) {
115
+ if (fd.isFile()) {
116
+ continue;
117
+ }
118
+
119
+ const contentTypeName = normalizeName(fd.name);
120
+ const contentType = await loadDir(join(dir, fd.name));
121
+
122
+ contentTypes[normalizeName(contentTypeName)] = _.defaults(contentType, DEFAULT_CONTENT_TYPE);
123
+ }
124
+
125
+ return contentTypes;
126
+ };
127
+
128
+ const loadDir = async dir => {
129
+ if (!(await fse.pathExists(dir))) {
130
+ return;
131
+ }
132
+
133
+ const fds = await fse.readdir(dir, { withFileTypes: true });
134
+
135
+ const root = {};
136
+ for (const fd of fds) {
137
+ if (!fd.isFile()) {
138
+ continue;
139
+ }
140
+
141
+ const key = basename(fd.name, extname(fd.name));
142
+ root[normalizeName(key)] = await loadFile(join(dir, fd.name));
143
+ }
144
+
145
+ return root;
146
+ };
147
+
148
+ const loadFile = file => {
149
+ const ext = extname(file);
150
+
151
+ switch (ext) {
152
+ case '.js':
153
+ return require(file);
154
+ case '.json':
155
+ return fse.readJSON(file);
156
+ default:
157
+ return {};
158
+ }
159
+ };
@@ -2,23 +2,21 @@
2
2
 
3
3
  const { join } = require('path');
4
4
  const _ = require('lodash');
5
- const { exists } = require('fs-extra');
6
- const loadFiles = require('../load/load-files');
5
+ const { pathExists } = require('fs-extra');
6
+ const loadFiles = require('../../load/load-files');
7
7
 
8
8
  module.exports = async strapi => {
9
- const componentsDir = join(strapi.dir, 'components');
10
-
11
- if (!(await exists(componentsDir))) {
9
+ if (!(await pathExists(strapi.dirs.components))) {
12
10
  return {};
13
11
  }
14
12
 
15
- const map = await loadFiles(componentsDir, '*/*.*(js|json)');
13
+ const map = await loadFiles(strapi.dirs.components, '*/*.*(js|json)');
16
14
 
17
15
  return Object.keys(map).reduce((acc, category) => {
18
16
  Object.keys(map[category]).forEach(key => {
19
17
  const schema = map[category][key];
20
18
 
21
- const filePath = join(componentsDir, category, schema.__filename__);
19
+ const filePath = join(strapi.dirs.components, category, schema.__filename__);
22
20
 
23
21
  if (!schema.collectionName) {
24
22
  return strapi.stopWithError(
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ loadSrcIndex: require('./src-index'),
5
+ loadAPIs: require('./apis'),
6
+ loadMiddlewares: require('./middlewares'),
7
+ loadComponents: require('./components'),
8
+ loadPolicies: require('./policies'),
9
+ loadPlugins: require('./plugins'),
10
+ loadAdmin: require('./admin'),
11
+ };
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ const { join, extname, basename } = require('path');
4
+ const fse = require('fs-extra');
5
+
6
+ // TODO:: allow folders with index.js inside for bigger policies
7
+ module.exports = async function loadMiddlewares(strapi) {
8
+ const localMiddlewares = await loadLocalMiddlewares(strapi);
9
+ const internalMiddlewares = require('../../middlewares');
10
+
11
+ strapi.container.get('middlewares').add(`global::`, localMiddlewares);
12
+ strapi.container.get('middlewares').add(`strapi::`, internalMiddlewares);
13
+ };
14
+
15
+ const loadLocalMiddlewares = async strapi => {
16
+ const dir = strapi.dirs.middlewares;
17
+
18
+ if (!(await fse.pathExists(dir))) {
19
+ return {};
20
+ }
21
+
22
+ const middlewares = {};
23
+ const paths = await fse.readdir(dir, { withFileTypes: true });
24
+
25
+ for (const fd of paths) {
26
+ const { name } = fd;
27
+ const fullPath = join(dir, name);
28
+
29
+ if (fd.isFile() && extname(name) === '.js') {
30
+ const key = basename(name, '.js');
31
+ middlewares[key] = require(fullPath);
32
+ }
33
+ }
34
+
35
+ return middlewares;
36
+ };