@strapi/strapi 4.0.0-next.9 → 4.0.3

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 (142) hide show
  1. package/README.md +14 -14
  2. package/bin/strapi.js +37 -6
  3. package/lib/Strapi.js +140 -72
  4. package/lib/commands/build.js +21 -11
  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 +24 -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 -19
  18. package/lib/core/bootstrap.js +3 -34
  19. package/lib/core/domain/content-type/index.js +3 -7
  20. package/lib/core/domain/module/index.js +8 -6
  21. package/lib/core/domain/module/validation.js +1 -4
  22. package/lib/core/loaders/admin.js +2 -2
  23. package/lib/core/loaders/apis.js +7 -7
  24. package/lib/core/loaders/components.js +3 -5
  25. package/lib/core/loaders/index.js +1 -0
  26. package/lib/core/loaders/middlewares.js +23 -123
  27. package/lib/core/loaders/plugins/get-enabled-plugins.js +55 -19
  28. package/lib/core/loaders/plugins/get-user-plugins-config.js +37 -0
  29. package/lib/core/loaders/plugins/index.js +30 -16
  30. package/lib/core/loaders/policies.js +1 -1
  31. package/lib/core/loaders/src-index.js +39 -0
  32. package/lib/core/registries/apis.js +2 -16
  33. package/lib/core/registries/content-types.js +50 -6
  34. package/lib/core/registries/controllers.d.ts +7 -0
  35. package/lib/core/registries/controllers.js +74 -3
  36. package/lib/core/registries/hooks.d.ts +20 -0
  37. package/lib/core/registries/hooks.js +87 -0
  38. package/lib/core/registries/middlewares.d.ts +5 -0
  39. package/lib/core/registries/middlewares.js +61 -2
  40. package/lib/core/registries/modules.js +3 -3
  41. package/lib/core/registries/plugins.js +2 -2
  42. package/lib/core/registries/policies.d.ts +9 -0
  43. package/lib/core/registries/policies.js +57 -6
  44. package/lib/core/registries/services.d.ts +7 -0
  45. package/lib/core/registries/services.js +71 -15
  46. package/lib/core-api/controller/collection-type.js +38 -11
  47. package/lib/core-api/controller/index.d.ts +25 -0
  48. package/lib/core-api/controller/index.js +30 -11
  49. package/lib/core-api/controller/single-type.js +26 -7
  50. package/lib/core-api/controller/transform.js +28 -3
  51. package/lib/core-api/routes/index.js +71 -0
  52. package/lib/core-api/service/collection-type.js +22 -27
  53. package/lib/core-api/service/index.d.ts +21 -0
  54. package/lib/core-api/service/index.js +9 -19
  55. package/lib/core-api/service/pagination.js +16 -16
  56. package/lib/core-api/service/single-type.js +17 -20
  57. package/lib/factories.d.ts +48 -0
  58. package/lib/factories.js +84 -0
  59. package/lib/index.d.ts +10 -31
  60. package/lib/index.js +5 -1
  61. package/lib/middlewares/body.js +33 -0
  62. package/lib/middlewares/compression.js +8 -0
  63. package/lib/middlewares/cors.js +58 -0
  64. package/lib/middlewares/errors.js +40 -0
  65. package/lib/middlewares/favicon.js +19 -0
  66. package/lib/middlewares/index.d.ts +5 -0
  67. package/lib/middlewares/index.js +30 -116
  68. package/lib/middlewares/ip.js +8 -0
  69. package/lib/middlewares/logger.js +27 -0
  70. package/lib/middlewares/powered-by.js +20 -0
  71. package/lib/middlewares/public/index.js +72 -77
  72. package/lib/middlewares/query.js +46 -0
  73. package/lib/middlewares/response-time.js +15 -0
  74. package/lib/middlewares/responses.js +19 -0
  75. package/lib/middlewares/security.js +51 -0
  76. package/lib/middlewares/session/index.js +6 -6
  77. package/lib/migrations/draft-publish.js +57 -0
  78. package/lib/services/auth/index.js +87 -0
  79. package/lib/services/core-store.js +64 -49
  80. package/lib/services/cron.js +54 -0
  81. package/lib/services/entity-service/attributes/index.js +31 -0
  82. package/lib/services/entity-service/attributes/transforms.js +20 -0
  83. package/lib/services/entity-service/components.js +39 -15
  84. package/lib/services/entity-service/index.d.ts +91 -0
  85. package/lib/services/entity-service/index.js +118 -60
  86. package/lib/services/entity-service/params.js +48 -81
  87. package/lib/services/entity-validator/index.js +76 -43
  88. package/lib/services/entity-validator/validators.js +131 -43
  89. package/lib/services/errors.js +77 -0
  90. package/lib/services/fs.js +1 -1
  91. package/lib/services/metrics/index.js +38 -36
  92. package/lib/services/server/admin-api.js +14 -0
  93. package/lib/services/server/api.js +36 -0
  94. package/lib/services/server/compose-endpoint.js +141 -0
  95. package/lib/services/server/content-api.js +16 -0
  96. package/lib/{server.js → services/server/http-server.js} +0 -0
  97. package/lib/services/server/index.js +127 -0
  98. package/lib/services/server/koa.js +64 -0
  99. package/lib/services/server/middleware.js +122 -0
  100. package/lib/services/server/policy.js +32 -0
  101. package/lib/services/server/register-middlewares.js +110 -0
  102. package/lib/services/server/register-routes.js +106 -0
  103. package/lib/services/server/routing.js +120 -0
  104. package/lib/services/webhook-runner.js +1 -1
  105. package/lib/utils/ee.js +3 -3
  106. package/lib/utils/get-dirs.js +17 -0
  107. package/lib/utils/index.js +2 -0
  108. package/lib/utils/signals.js +24 -0
  109. package/lib/utils/update-notifier/index.js +2 -1
  110. package/package.json +94 -93
  111. package/lib/core/app-configuration/load-functions.js +0 -28
  112. package/lib/core-api/index.js +0 -39
  113. package/lib/middlewares/boom/defaults.json +0 -5
  114. package/lib/middlewares/boom/index.js +0 -147
  115. package/lib/middlewares/cors/index.js +0 -66
  116. package/lib/middlewares/cron/defaults.json +0 -5
  117. package/lib/middlewares/cron/index.js +0 -43
  118. package/lib/middlewares/favicon/defaults.json +0 -7
  119. package/lib/middlewares/favicon/index.js +0 -32
  120. package/lib/middlewares/gzip/defaults.json +0 -6
  121. package/lib/middlewares/gzip/index.js +0 -19
  122. package/lib/middlewares/helmet/defaults.json +0 -18
  123. package/lib/middlewares/helmet/index.js +0 -9
  124. package/lib/middlewares/ip/defaults.json +0 -7
  125. package/lib/middlewares/ip/index.js +0 -25
  126. package/lib/middlewares/language/defaults.json +0 -9
  127. package/lib/middlewares/language/index.js +0 -40
  128. package/lib/middlewares/logger/defaults.json +0 -5
  129. package/lib/middlewares/logger/index.js +0 -37
  130. package/lib/middlewares/parser/defaults.json +0 -11
  131. package/lib/middlewares/parser/index.js +0 -72
  132. package/lib/middlewares/poweredBy/defaults.json +0 -5
  133. package/lib/middlewares/poweredBy/index.js +0 -16
  134. package/lib/middlewares/public/defaults.json +0 -8
  135. package/lib/middlewares/responseTime/defaults.json +0 -5
  136. package/lib/middlewares/responseTime/index.js +0 -25
  137. package/lib/middlewares/responses/defaults.json +0 -5
  138. package/lib/middlewares/responses/index.js +0 -18
  139. package/lib/middlewares/router/defaults.json +0 -7
  140. package/lib/middlewares/router/index.js +0 -72
  141. package/lib/middlewares/router/utils/compose-endpoint.js +0 -169
  142. package/lib/utils/get-prefixed-dependencies.js +0 -7
@@ -1,136 +1,36 @@
1
1
  'use strict';
2
2
 
3
- // Dependencies.
4
- const path = require('path');
5
- const fs = require('fs-extra');
6
- const _ = require('lodash');
7
- const glob = require('../../load/glob');
8
- const findPackagePath = require('../../load/package-path');
3
+ const { join, extname, basename } = require('path');
4
+ const fse = require('fs-extra');
9
5
 
10
- /**
11
- * Load middlewares
12
- */
13
- module.exports = async function(strapi) {
14
- const installedMiddlewares = strapi.config.get('installedMiddlewares');
15
- const appPath = strapi.config.get('appPath');
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');
16
10
 
17
- let middlewares = {};
18
-
19
- const loaders = createLoaders(strapi);
20
-
21
- await loaders.loadMiddlewareDependencies(installedMiddlewares, middlewares);
22
- // internal middlewares
23
- await loaders.loadInternalMiddlewares(middlewares);
24
- // local middleware
25
- await loaders.loadLocalMiddlewares(appPath, middlewares);
26
- // plugins middlewares
27
- await loaders.loadPluginsMiddlewares(strapi.plugins, middlewares);
28
- // local plugin middlewares
29
- await loaders.loadLocalPluginsMiddlewares(appPath, middlewares);
30
- // load admin middlewares
31
- await loaders.loadAdminMiddlewares(middlewares);
32
-
33
- return middlewares;
11
+ strapi.container.get('middlewares').add(`global::`, localMiddlewares);
12
+ strapi.container.get('middlewares').add(`strapi::`, internalMiddlewares);
34
13
  };
35
14
 
36
- /**
37
- * Build loader functions
38
- * @param {*} strapi - strapi instance
39
- */
40
- const createLoaders = strapi => {
41
- const loadMiddlewaresInDir = async (dir, middlewares) => {
42
- const files = await glob('*/*(index|defaults).*(js|json)', {
43
- cwd: dir,
44
- });
45
-
46
- files.forEach(f => {
47
- const name = f.split('/')[0];
48
- mountMiddleware(name, [path.resolve(dir, f)], middlewares);
49
- });
50
- };
15
+ const loadLocalMiddlewares = async strapi => {
16
+ const dir = strapi.dirs.middlewares;
51
17
 
52
- const loadInternalMiddlewares = middlewares =>
53
- loadMiddlewaresInDir(path.resolve(__dirname, '..', '..', 'middlewares'), middlewares);
18
+ if (!(await fse.pathExists(dir))) {
19
+ return {};
20
+ }
54
21
 
55
- const loadLocalMiddlewares = (appPath, middlewares) =>
56
- loadMiddlewaresInDir(path.resolve(appPath, 'middlewares'), middlewares);
22
+ const middlewares = {};
23
+ const paths = await fse.readdir(dir, { withFileTypes: true });
57
24
 
58
- const loadPluginsMiddlewares = async (plugins, middlewares) => {
59
- for (const pluginName in plugins) {
60
- const pluginMiddlewares = plugins[pluginName].middlewares;
61
- for (const middlewareName in pluginMiddlewares) {
62
- middlewares[middlewareName] = {
63
- loaded: false,
64
- ...pluginMiddlewares[middlewareName],
65
- };
66
- }
67
- }
68
- };
69
-
70
- const loadLocalPluginsMiddlewares = async (appPath, middlewares) => {
71
- const pluginsDir = path.resolve(appPath, 'plugins');
72
- if (!fs.existsSync(pluginsDir)) return;
73
-
74
- const pluginsNames = await fs.readdir(pluginsDir);
25
+ for (const fd of paths) {
26
+ const { name } = fd;
27
+ const fullPath = join(dir, name);
75
28
 
76
- for (let pluginFolder of pluginsNames) {
77
- // ignore files
78
- const stat = await fs.stat(path.resolve(pluginsDir, pluginFolder));
79
- if (!stat.isDirectory()) continue;
80
-
81
- const dir = path.resolve(pluginsDir, pluginFolder, 'middlewares');
82
- await loadMiddlewaresInDir(dir, middlewares);
29
+ if (fd.isFile() && extname(name) === '.js') {
30
+ const key = basename(name, '.js');
31
+ middlewares[key] = require(fullPath);
83
32
  }
84
- };
85
-
86
- const loadAdminMiddlewares = async middlewares => {
87
- const middlewaresDir = 'middlewares';
88
- const dir = path.resolve(findPackagePath(`@strapi/admin`), middlewaresDir);
89
- await loadMiddlewaresInDir(dir, middlewares);
33
+ }
90
34
 
91
- // load ee admin middlewares if they exist
92
- if (process.env.STRAPI_DISABLE_EE !== 'true' && strapi.EE) {
93
- await loadMiddlewaresInDir(`${dir}/../ee/${middlewaresDir}`, middlewares);
94
- }
95
- };
96
-
97
- const loadMiddlewareDependencies = async (packages, middlewares) => {
98
- for (let packageName of packages) {
99
- const baseDir = path.dirname(require.resolve(`@strapi/middleware-${packageName}`));
100
- const files = await glob('*(index|defaults).*(js|json)', {
101
- cwd: baseDir,
102
- absolute: true,
103
- });
104
-
105
- mountMiddleware(packageName, files, middlewares);
106
- }
107
- };
108
-
109
- const mountMiddleware = (name, files, middlewares) => {
110
- files.forEach(file => {
111
- middlewares[name] = middlewares[name] || { loaded: false };
112
-
113
- if (_.endsWith(file, 'index.js') && !middlewares[name].load) {
114
- return Object.defineProperty(middlewares[name], 'load', {
115
- configurable: false,
116
- enumerable: true,
117
- get: () => require(file)(strapi),
118
- });
119
- }
120
-
121
- if (_.endsWith(file, 'defaults.json')) {
122
- middlewares[name].defaults = require(file);
123
- return;
124
- }
125
- });
126
- };
127
-
128
- return {
129
- loadInternalMiddlewares,
130
- loadLocalMiddlewares,
131
- loadPluginsMiddlewares,
132
- loadLocalPluginsMiddlewares,
133
- loadMiddlewareDependencies,
134
- loadAdminMiddlewares,
135
- };
35
+ return middlewares;
136
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
7
  const loadConfigFile = require('../../app-configuration/load-config-file');
8
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,15 +89,27 @@ 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
 
@@ -5,7 +5,7 @@ const fse = require('fs-extra');
5
5
 
6
6
  // TODO:: allow folders with index.js inside for bigger policies
7
7
  module.exports = async function loadPolicies(strapi) {
8
- const dir = join(strapi.dir, 'policies');
8
+ const dir = strapi.dirs.policies;
9
9
 
10
10
  if (!(await fse.pathExists(dir))) {
11
11
  return;
@@ -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
+ };
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const { has } = require('lodash/fp');
4
- const { createCoreApi } = require('../../core-api');
5
4
 
6
5
  const apisRegistry = strapi => {
7
6
  const apis = {};
@@ -18,22 +17,9 @@ const apisRegistry = strapi => {
18
17
  throw new Error(`API ${apiName} has already been registered.`);
19
18
  }
20
19
 
21
- const apiInstance = strapi.container.get('modules').add(`api::${apiName}`, apiConfig);
20
+ const api = strapi.container.get('modules').add(`api::${apiName}`, apiConfig);
22
21
 
23
- for (const ctName in apiInstance.contentTypes || {}) {
24
- const contentType = apiInstance.contentTypes[ctName];
25
-
26
- const { service, controller } = createCoreApi({
27
- model: contentType,
28
- api: apiInstance,
29
- strapi,
30
- });
31
-
32
- strapi.container.get('services').set(`api::${apiName}.${ctName}`, service);
33
- strapi.container.get('controllers').set(`api::${apiName}.${ctName}`, controller);
34
- }
35
-
36
- apis[apiName] = apiInstance;
22
+ apis[apiName] = api;
37
23
 
38
24
  return apis[apiName];
39
25
  },
@@ -20,32 +20,76 @@ const contentTypesRegistry = () => {
20
20
  const contentTypes = {};
21
21
 
22
22
  return {
23
- get(ctUID) {
24
- return contentTypes[ctUID];
23
+ /**
24
+ * Returns this list of registered contentTypes uids
25
+ * @returns {string[]}
26
+ */
27
+ keys() {
28
+ return Object.keys(contentTypes);
25
29
  },
30
+
31
+ /**
32
+ * Returns the instance of a contentType. Instantiate the contentType if not already done
33
+ * @param {string} uid
34
+ * @returns
35
+ */
36
+ get(uid) {
37
+ return contentTypes[uid];
38
+ },
39
+
40
+ /**
41
+ * Returns a map with all the contentTypes in a namespace
42
+ * @param {string} namespace
43
+ */
26
44
  getAll(namespace) {
27
45
  return pickBy((_, uid) => hasNamespace(uid, namespace))(contentTypes);
28
46
  },
29
- add(namespace, rawContentTypes) {
30
- validateKeySameToSingularName(rawContentTypes);
31
47
 
32
- for (const rawCtName in rawContentTypes) {
48
+ /**
49
+ * Registers a contentType
50
+ * @param {string} uid
51
+ * @param {Object} contentType
52
+ */
53
+ set(uid, contentType) {
54
+ contentTypes[uid] = contentType;
55
+ return this;
56
+ },
57
+
58
+ /**
59
+ * Registers a map of contentTypes for a specific namespace
60
+ * @param {string} namespace
61
+ * @param {{ [key: string]: Object }} newContentTypes
62
+ */
63
+ add(namespace, newContentTypes) {
64
+ validateKeySameToSingularName(newContentTypes);
65
+
66
+ for (const rawCtName in newContentTypes) {
33
67
  const uid = addNamespace(rawCtName, namespace);
34
68
 
35
69
  if (has(uid, contentTypes)) {
36
70
  throw new Error(`Content-type ${uid} has already been registered.`);
37
71
  }
38
72
 
39
- contentTypes[uid] = createContentType(uid, rawContentTypes[rawCtName]);
73
+ contentTypes[uid] = createContentType(uid, newContentTypes[rawCtName]);
40
74
  }
41
75
  },
76
+
77
+ /**
78
+ * Wraps a contentType to extend it
79
+ * @param {string} uid
80
+ * @param {(contentType: Object) => Object} extendFn
81
+ */
42
82
  extend(ctUID, extendFn) {
43
83
  const currentContentType = this.get(ctUID);
84
+
44
85
  if (!currentContentType) {
45
86
  throw new Error(`Content-Type ${ctUID} doesn't exist`);
46
87
  }
88
+
47
89
  const newContentType = extendFn(currentContentType);
48
90
  contentTypes[ctUID] = newContentType;
91
+
92
+ return this;
49
93
  },
50
94
  };
51
95
  };
@@ -0,0 +1,7 @@
1
+ import { Strapi } from '../../';
2
+
3
+ export type Controller = {
4
+ [key: string]: (...args: any) => any;
5
+ };
6
+
7
+ export type ControllerFactory = ({ strapi: Strapi }) => Controller;