@strapi/strapi 4.0.0-next.7 → 4.0.1

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 (157) hide show
  1. package/README.md +14 -14
  2. package/bin/strapi.js +46 -60
  3. package/lib/Strapi.js +193 -98
  4. package/lib/commands/build.js +19 -8
  5. package/lib/commands/console.js +1 -1
  6. package/lib/commands/content-types/list.js +22 -0
  7. package/lib/commands/controllers/list.js +22 -0
  8. package/lib/commands/develop.js +22 -27
  9. package/lib/commands/generate-template.js +4 -5
  10. package/lib/commands/hooks/list.js +22 -0
  11. package/lib/commands/middlewares/list.js +22 -0
  12. package/lib/commands/new.js +3 -1
  13. package/lib/commands/policies/list.js +22 -0
  14. package/lib/commands/routes/list.js +28 -0
  15. package/lib/commands/services/list.js +22 -0
  16. package/lib/commands/watchAdmin.js +18 -9
  17. package/lib/core/app-configuration/index.js +3 -36
  18. package/lib/core/bootstrap.js +25 -0
  19. package/lib/core/domain/content-type/index.js +26 -29
  20. package/lib/core/domain/content-type/validator.js +22 -3
  21. package/lib/core/domain/module/index.js +42 -11
  22. package/lib/core/domain/module/validation.js +16 -19
  23. package/lib/core/loaders/admin.js +16 -0
  24. package/lib/core/loaders/apis.js +159 -0
  25. package/lib/core/loaders/{load-components.js → components.js} +5 -6
  26. package/lib/core/loaders/index.js +11 -0
  27. package/lib/core/loaders/middlewares.js +36 -0
  28. package/lib/core/{load-plugins → loaders/plugins}/get-enabled-plugins.js +55 -19
  29. package/lib/core/loaders/plugins/get-user-plugins-config.js +37 -0
  30. package/lib/core/{load-plugins → loaders/plugins}/index.js +35 -19
  31. package/lib/core/loaders/policies.js +28 -0
  32. package/lib/core/loaders/src-index.js +39 -0
  33. package/lib/core/registries/apis.js +29 -0
  34. package/lib/core/registries/content-types.js +66 -10
  35. package/lib/core/registries/controllers.d.ts +7 -0
  36. package/lib/core/registries/controllers.js +92 -7
  37. package/lib/core/registries/hooks.d.ts +20 -0
  38. package/lib/core/registries/hooks.js +87 -0
  39. package/lib/core/registries/middlewares.d.ts +5 -0
  40. package/lib/core/registries/middlewares.js +65 -5
  41. package/lib/core/registries/modules.js +3 -3
  42. package/lib/core/registries/plugins.js +2 -2
  43. package/lib/core/registries/policies.d.ts +9 -0
  44. package/lib/core/registries/policies.js +65 -5
  45. package/lib/core/registries/services.d.ts +7 -0
  46. package/lib/core/registries/services.js +88 -17
  47. package/lib/core/utils.js +35 -0
  48. package/lib/core-api/controller/collection-type.js +45 -26
  49. package/lib/core-api/controller/index.d.ts +25 -0
  50. package/lib/core-api/controller/index.js +33 -11
  51. package/lib/core-api/controller/single-type.js +29 -15
  52. package/lib/core-api/controller/transform.js +62 -6
  53. package/lib/core-api/routes/index.js +71 -0
  54. package/lib/core-api/service/collection-type.js +43 -21
  55. package/lib/core-api/service/index.d.ts +21 -0
  56. package/lib/core-api/service/index.js +8 -67
  57. package/lib/core-api/service/pagination.js +125 -0
  58. package/lib/core-api/service/single-type.js +17 -19
  59. package/lib/factories.d.ts +48 -0
  60. package/lib/factories.js +84 -0
  61. package/lib/index.d.ts +10 -31
  62. package/lib/index.js +5 -1
  63. package/lib/middlewares/body.js +33 -0
  64. package/lib/middlewares/compression.js +8 -0
  65. package/lib/middlewares/cors.js +58 -0
  66. package/lib/middlewares/errors.js +40 -0
  67. package/lib/middlewares/favicon.js +19 -0
  68. package/lib/middlewares/index.d.ts +5 -0
  69. package/lib/middlewares/index.js +30 -116
  70. package/lib/middlewares/ip.js +8 -0
  71. package/lib/middlewares/logger.js +27 -0
  72. package/lib/middlewares/powered-by.js +20 -0
  73. package/lib/middlewares/public/index.js +72 -77
  74. package/lib/middlewares/query.js +46 -0
  75. package/lib/middlewares/response-time.js +15 -0
  76. package/lib/middlewares/responses.js +19 -0
  77. package/lib/middlewares/security.js +51 -0
  78. package/lib/middlewares/session/index.js +6 -6
  79. package/lib/migrations/draft-publish.js +57 -0
  80. package/lib/services/auth/index.js +87 -0
  81. package/lib/services/core-store.js +64 -51
  82. package/lib/services/cron.js +54 -0
  83. package/lib/services/entity-service/attributes/index.js +31 -0
  84. package/lib/services/entity-service/attributes/transforms.js +20 -0
  85. package/lib/services/entity-service/components.js +39 -15
  86. package/lib/services/entity-service/index.d.ts +91 -0
  87. package/lib/services/entity-service/index.js +120 -59
  88. package/lib/services/entity-service/params.js +52 -94
  89. package/lib/services/entity-validator/index.js +76 -43
  90. package/lib/services/entity-validator/validators.js +131 -43
  91. package/lib/services/errors.js +77 -0
  92. package/lib/{core → services}/fs.js +1 -1
  93. package/lib/services/metrics/index.js +38 -36
  94. package/lib/services/server/admin-api.js +14 -0
  95. package/lib/services/server/api.js +36 -0
  96. package/lib/services/server/compose-endpoint.js +141 -0
  97. package/lib/services/server/content-api.js +16 -0
  98. package/lib/{server.js → services/server/http-server.js} +0 -0
  99. package/lib/services/server/index.js +127 -0
  100. package/lib/services/server/koa.js +64 -0
  101. package/lib/services/server/middleware.js +122 -0
  102. package/lib/services/server/policy.js +32 -0
  103. package/lib/services/server/register-middlewares.js +110 -0
  104. package/lib/services/server/register-routes.js +106 -0
  105. package/lib/services/server/routing.js +120 -0
  106. package/lib/services/webhook-runner.js +1 -1
  107. package/lib/utils/ee.js +3 -3
  108. package/lib/utils/get-dirs.js +17 -0
  109. package/lib/utils/index.js +2 -0
  110. package/lib/utils/is-initialized.js +1 -1
  111. package/lib/utils/signals.js +24 -0
  112. package/lib/utils/startup-logger.js +2 -2
  113. package/lib/utils/update-notifier/index.js +3 -2
  114. package/package.json +94 -97
  115. package/lib/commands/generate.js +0 -76
  116. package/lib/core/loaders/bootstrap.js +0 -176
  117. package/lib/core/loaders/load-apis.js +0 -20
  118. package/lib/core/loaders/load-functions.js +0 -21
  119. package/lib/core/loaders/load-middlewares.js +0 -136
  120. package/lib/core/loaders/load-modules.js +0 -21
  121. package/lib/core/loaders/load-policies.js +0 -36
  122. package/lib/core/loaders/walk.js +0 -27
  123. package/lib/core-api/index.js +0 -39
  124. package/lib/load/check-reserved-filename.js +0 -10
  125. package/lib/load/load-config-files.js +0 -22
  126. package/lib/load/require-file-parse.js +0 -15
  127. package/lib/middlewares/boom/defaults.json +0 -5
  128. package/lib/middlewares/boom/index.js +0 -147
  129. package/lib/middlewares/cors/index.js +0 -66
  130. package/lib/middlewares/cron/defaults.json +0 -5
  131. package/lib/middlewares/cron/index.js +0 -43
  132. package/lib/middlewares/favicon/defaults.json +0 -7
  133. package/lib/middlewares/favicon/index.js +0 -32
  134. package/lib/middlewares/gzip/defaults.json +0 -6
  135. package/lib/middlewares/gzip/index.js +0 -19
  136. package/lib/middlewares/helmet/defaults.json +0 -18
  137. package/lib/middlewares/helmet/index.js +0 -9
  138. package/lib/middlewares/ip/defaults.json +0 -7
  139. package/lib/middlewares/ip/index.js +0 -25
  140. package/lib/middlewares/language/defaults.json +0 -9
  141. package/lib/middlewares/language/index.js +0 -40
  142. package/lib/middlewares/logger/defaults.json +0 -5
  143. package/lib/middlewares/logger/index.js +0 -37
  144. package/lib/middlewares/parser/defaults.json +0 -11
  145. package/lib/middlewares/parser/index.js +0 -71
  146. package/lib/middlewares/poweredBy/defaults.json +0 -5
  147. package/lib/middlewares/poweredBy/index.js +0 -16
  148. package/lib/middlewares/public/defaults.json +0 -8
  149. package/lib/middlewares/responseTime/defaults.json +0 -5
  150. package/lib/middlewares/responseTime/index.js +0 -25
  151. package/lib/middlewares/responses/defaults.json +0 -5
  152. package/lib/middlewares/responses/index.js +0 -18
  153. package/lib/middlewares/router/defaults.json +0 -7
  154. package/lib/middlewares/router/index.js +0 -56
  155. package/lib/middlewares/router/utils/composeEndpoint.js +0 -25
  156. package/lib/middlewares/router/utils/routerChecker.js +0 -96
  157. package/lib/utils/get-prefixed-dependencies.js +0 -7
@@ -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');
5
+ const { pathExists } = require('fs-extra');
6
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(
@@ -32,6 +30,7 @@ module.exports = async strapi => {
32
30
  __schema__: _.cloneDeep(schema),
33
31
  uid,
34
32
  category,
33
+ modelType: 'component',
35
34
  modelName: key,
36
35
  globalId: schema.globalId || _.upperFirst(_.camelCase(`component_${uid}`)),
37
36
  });
@@ -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
+ };
@@ -1,13 +1,19 @@
1
1
  'use strict';
2
2
 
3
- const { dirname, join } = require('path');
3
+ const { dirname, join, resolve } = require('path');
4
4
  const { statSync, existsSync } = require('fs');
5
5
  const _ = require('lodash');
6
6
  const { get, has, pick, pickBy, defaultsDeep, map, prop, pipe } = require('lodash/fp');
7
7
  const { isKebabCase } = require('@strapi/utils');
8
- const loadConfigFile = require('../app-configuration/load-config-file');
8
+ const getUserPluginsConfig = require('./get-user-plugins-config');
9
9
 
10
10
  const isStrapiPlugin = info => get('strapi.kind', info) === 'plugin';
11
+ const INTERNAL_PLUGINS = [
12
+ '@strapi/plugin-content-manager',
13
+ '@strapi/plugin-content-type-builder',
14
+ '@strapi/plugin-email',
15
+ '@strapi/plugin-upload',
16
+ ];
11
17
 
12
18
  const validatePluginName = pluginName => {
13
19
  if (!isKebabCase(pluginName)) {
@@ -21,17 +27,14 @@ const toDetailedDeclaration = declaration => {
21
27
  }
22
28
 
23
29
  let detailedDeclaration = pick(['enabled'], declaration);
24
- if (has('config', declaration)) {
25
- detailedDeclaration.userConfig = declaration.config;
26
- }
27
30
  if (has('resolve', declaration)) {
28
31
  let pathToPlugin = '';
29
32
  try {
30
33
  pathToPlugin = dirname(require.resolve(declaration.resolve));
31
34
  } catch (e) {
32
- if (existsSync(declaration.resolve) && statSync(declaration.resolve).isDirectory()) {
33
- pathToPlugin = declaration.resolve;
34
- } else {
35
+ pathToPlugin = resolve(strapi.dirs.root, declaration.resolve);
36
+
37
+ if (!existsSync(pathToPlugin) || !statSync(pathToPlugin).isDirectory()) {
35
38
  throw new Error(`${declaration.resolve} couldn't be resolved`);
36
39
  }
37
40
  }
@@ -42,28 +45,59 @@ const toDetailedDeclaration = declaration => {
42
45
  };
43
46
 
44
47
  const getEnabledPlugins = async strapi => {
48
+ const internalPlugins = {};
49
+ for (const dep of INTERNAL_PLUGINS) {
50
+ const packagePath = join(dep, 'package.json');
51
+ const packageInfo = require(packagePath);
52
+
53
+ validatePluginName(packageInfo.strapi.name);
54
+ internalPlugins[packageInfo.strapi.name] = {
55
+ ...toDetailedDeclaration({ enabled: true, resolve: packagePath }),
56
+ info: packageInfo.strapi,
57
+ };
58
+ }
59
+
45
60
  const installedPlugins = {};
46
61
  for (const dep in strapi.config.get('info.dependencies', {})) {
47
62
  const packagePath = join(dep, 'package.json');
48
- const packageInfo = require(packagePath);
63
+ let packageInfo;
64
+ try {
65
+ packageInfo = require(packagePath);
66
+ } catch {
67
+ continue;
68
+ }
49
69
 
50
70
  if (isStrapiPlugin(packageInfo)) {
51
71
  validatePluginName(packageInfo.strapi.name);
52
- installedPlugins[packageInfo.strapi.name] = toDetailedDeclaration({
53
- enabled: true,
54
- resolve: packagePath,
55
- });
72
+ installedPlugins[packageInfo.strapi.name] = {
73
+ ...toDetailedDeclaration({ enabled: true, resolve: packagePath }),
74
+ info: packageInfo.strapi,
75
+ };
56
76
  }
57
77
  }
58
78
 
59
79
  const declaredPlugins = {};
60
- const userPluginConfigPath = join(strapi.dir, 'config', 'plugins.js');
61
- const userPluginsConfig = existsSync(userPluginConfigPath)
62
- ? loadConfigFile(userPluginConfigPath)
63
- : {};
80
+ const userPluginsConfig = await getUserPluginsConfig();
81
+
64
82
  _.forEach(userPluginsConfig, (declaration, pluginName) => {
65
83
  validatePluginName(pluginName);
66
- declaredPlugins[pluginName] = toDetailedDeclaration(declaration);
84
+
85
+ declaredPlugins[pluginName] = {
86
+ ...toDetailedDeclaration(declaration),
87
+ info: {},
88
+ };
89
+
90
+ const { pathToPlugin } = declaredPlugins[pluginName];
91
+
92
+ // for manually resolved plugins
93
+ if (pathToPlugin) {
94
+ const packagePath = join(pathToPlugin, 'package.json');
95
+ const packageInfo = require(packagePath);
96
+
97
+ if (isStrapiPlugin(packageInfo)) {
98
+ declaredPlugins[pluginName].info = packageInfo.strapi || {};
99
+ }
100
+ }
67
101
  });
68
102
 
69
103
  const declaredPluginsResolves = map(prop('pathToPlugin'), declaredPlugins);
@@ -71,10 +105,12 @@ const getEnabledPlugins = async strapi => {
71
105
  p => !declaredPluginsResolves.includes(p.pathToPlugin),
72
106
  installedPlugins
73
107
  );
108
+
74
109
  const enabledPlugins = pipe(
110
+ defaultsDeep(declaredPlugins),
75
111
  defaultsDeep(installedPluginsNotAlreadyUsed),
76
112
  pickBy(p => p.enabled)
77
- )(declaredPlugins);
113
+ )(internalPlugins);
78
114
 
79
115
  return enabledPlugins;
80
116
  };
@@ -0,0 +1,37 @@
1
+ 'use strict';
2
+
3
+ const { join } = require('path');
4
+ const fse = require('fs-extra');
5
+ const { merge } = require('lodash/fp');
6
+ const loadConfigFile = require('../../app-configuration/load-config-file');
7
+
8
+ /**
9
+ * Return user defined plugins' config
10
+ * first load config from `config/plugins.js`
11
+ * and then merge config from `config/env/{env}/plugins.js`
12
+ * @return {Promise<{}>}
13
+ */
14
+ const getUserPluginsConfig = async () => {
15
+ const globalUserConfigPath = join(strapi.dirs.config, 'plugins.js');
16
+ const currentEnvUserConfigPath = join(
17
+ strapi.dirs.config,
18
+ 'env',
19
+ process.env.NODE_ENV,
20
+ 'plugins.js'
21
+ );
22
+ let config = {};
23
+
24
+ // assign global user config if exists
25
+ if (await fse.pathExists(globalUserConfigPath)) {
26
+ config = loadConfigFile(globalUserConfigPath);
27
+ }
28
+
29
+ // and merge user config by environment if exists
30
+ if (await fse.pathExists(currentEnvUserConfigPath)) {
31
+ config = merge(config, loadConfigFile(currentEnvUserConfigPath));
32
+ }
33
+
34
+ return config;
35
+ };
36
+
37
+ module.exports = getUserPluginsConfig;
@@ -1,20 +1,21 @@
1
1
  'use strict';
2
2
 
3
- const { join, resolve } = require('path');
4
- const { existsSync } = require('fs');
3
+ const { join } = require('path');
4
+ const fse = require('fs-extra');
5
5
  const { defaultsDeep, getOr, get } = require('lodash/fp');
6
6
  const { env } = require('@strapi/utils');
7
- const loadConfigFile = require('../app-configuration/load-config-file');
8
- const loadFiles = require('../../load/load-files');
7
+ const loadConfigFile = require('../../app-configuration/load-config-file');
8
+ const loadFiles = require('../../../load/load-files');
9
9
  const getEnabledPlugins = require('./get-enabled-plugins');
10
+ const getUserPluginsConfig = require('./get-user-plugins-config');
10
11
 
11
12
  const defaultPlugin = {
12
- bootstrap: () => {},
13
- destroy: () => {},
14
- register: () => {},
13
+ bootstrap() {},
14
+ destroy() {},
15
+ register() {},
15
16
  config: {
16
17
  default: {},
17
- validator: () => {},
18
+ validator() {},
18
19
  },
19
20
  routes: [],
20
21
  controllers: {},
@@ -25,8 +26,8 @@ const defaultPlugin = {
25
26
  };
26
27
 
27
28
  const applyUserExtension = async plugins => {
28
- const extensionsDir = resolve(strapi.dir, 'extensions');
29
- if (!existsSync(extensionsDir)) {
29
+ const extensionsDir = strapi.dirs.extensions;
30
+ if (!(await fse.pathExists(extensionsDir))) {
30
31
  return;
31
32
  }
32
33
 
@@ -39,7 +40,11 @@ const applyUserExtension = async plugins => {
39
40
  for (const ctName in plugin.contentTypes) {
40
41
  const extendedSchema = get([pluginName, 'content-types', ctName, 'schema'], extendedSchemas);
41
42
  if (extendedSchema) {
42
- plugin.contentTypes[ctName].schema = extendedSchema;
43
+ plugin.contentTypes[ctName].schema = Object.assign(
44
+ {},
45
+ plugin.contentTypes[ctName].schema,
46
+ extendedSchema
47
+ );
43
48
  }
44
49
  }
45
50
  // second: execute strapi-server extension
@@ -61,11 +66,8 @@ const formatContentTypes = plugins => {
61
66
  }
62
67
  };
63
68
 
64
- const applyUserConfig = plugins => {
65
- const userPluginConfigPath = join(strapi.dir, 'config', 'plugins.js');
66
- const userPluginsConfig = existsSync(userPluginConfigPath)
67
- ? loadConfigFile(userPluginConfigPath)
68
- : {};
69
+ const applyUserConfig = async plugins => {
70
+ const userPluginsConfig = await getUserPluginsConfig();
69
71
 
70
72
  for (const pluginName in plugins) {
71
73
  const plugin = plugins[pluginName];
@@ -87,19 +89,33 @@ const applyUserConfig = plugins => {
87
89
 
88
90
  const loadPlugins = async strapi => {
89
91
  const plugins = {};
92
+
90
93
  const enabledPlugins = await getEnabledPlugins(strapi);
91
94
 
95
+ strapi.config.set('enabledPlugins', enabledPlugins);
96
+
92
97
  for (const pluginName in enabledPlugins) {
93
98
  const enabledPlugin = enabledPlugins[pluginName];
94
- const pluginServer = loadConfigFile(join(enabledPlugin.pathToPlugin, 'strapi-server.js'));
99
+
100
+ const serverEntrypointPath = join(enabledPlugin.pathToPlugin, 'strapi-server.js');
101
+
102
+ // only load plugins with a server entrypoint
103
+ if (!(await fse.pathExists(serverEntrypointPath))) {
104
+ continue;
105
+ }
106
+
107
+ const pluginServer = loadConfigFile(serverEntrypointPath);
95
108
  plugins[pluginName] = defaultsDeep(defaultPlugin, pluginServer);
96
109
  }
110
+
97
111
  // TODO: validate plugin format
98
- applyUserConfig(plugins);
112
+ await applyUserConfig(plugins);
99
113
  await applyUserExtension(plugins);
100
114
  formatContentTypes(plugins);
101
115
 
102
- return plugins;
116
+ for (const pluginName in plugins) {
117
+ strapi.container.get('plugins').add(pluginName, plugins[pluginName]);
118
+ }
103
119
  };
104
120
 
105
121
  module.exports = loadPlugins;
@@ -0,0 +1,28 @@
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 loadPolicies(strapi) {
8
+ const dir = strapi.dirs.policies;
9
+
10
+ if (!(await fse.pathExists(dir))) {
11
+ return;
12
+ }
13
+
14
+ const policies = {};
15
+ const paths = await fse.readdir(dir, { withFileTypes: true });
16
+
17
+ for (const fd of paths) {
18
+ const { name } = fd;
19
+ const fullPath = join(dir, name);
20
+
21
+ if (fd.isFile() && extname(name) === '.js') {
22
+ const key = basename(name, '.js');
23
+ policies[key] = require(fullPath);
24
+ }
25
+ }
26
+
27
+ strapi.container.get('policies').add(`global::`, policies);
28
+ };
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ const { resolve } = require('path');
4
+ const { statSync, existsSync } = require('fs');
5
+ const { yup } = require('@strapi/utils');
6
+
7
+ const srcSchema = yup
8
+ .object()
9
+ .shape({
10
+ bootstrap: yup.mixed().isFunction(),
11
+ register: yup.mixed().isFunction(),
12
+ destroy: yup.mixed().isFunction(),
13
+ })
14
+ .noUnknown();
15
+
16
+ const validateSrcIndex = srcIndex => {
17
+ return srcSchema.validateSync(srcIndex, { strict: true, abortEarly: false });
18
+ };
19
+
20
+ module.exports = strapi => {
21
+ if (!existsSync(strapi.dirs.src)) {
22
+ throw new Error('Missing src folder. Please create one at `./src`');
23
+ }
24
+
25
+ const pathToSrcIndex = resolve(strapi.dirs.src, 'index.js');
26
+ if (!existsSync(pathToSrcIndex) || statSync(pathToSrcIndex).isDirectory()) {
27
+ return {};
28
+ }
29
+
30
+ const srcIndex = require(pathToSrcIndex);
31
+
32
+ try {
33
+ validateSrcIndex(srcIndex);
34
+ } catch (e) {
35
+ strapi.stopWithError({ message: `Invalid file \`./src/index.js\`: ${e.message}` });
36
+ }
37
+
38
+ return srcIndex;
39
+ };
@@ -0,0 +1,29 @@
1
+ 'use strict';
2
+
3
+ const { has } = require('lodash/fp');
4
+
5
+ const apisRegistry = strapi => {
6
+ const apis = {};
7
+
8
+ return {
9
+ get(name) {
10
+ return apis[name];
11
+ },
12
+ getAll() {
13
+ return apis;
14
+ },
15
+ add(apiName, apiConfig) {
16
+ if (has(apiName, apis)) {
17
+ throw new Error(`API ${apiName} has already been registered.`);
18
+ }
19
+
20
+ const api = strapi.container.get('modules').add(`api::${apiName}`, apiConfig);
21
+
22
+ apis[apiName] = api;
23
+
24
+ return apis[apiName];
25
+ },
26
+ };
27
+ };
28
+
29
+ module.exports = apisRegistry;