@strapi/strapi 4.0.0-next.9 → 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 (140) hide show
  1. package/README.md +12 -12
  2. package/bin/strapi.js +32 -5
  3. package/lib/Strapi.js +140 -72
  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 +14 -15
  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 -9
  16. package/lib/core/app-configuration/index.js +3 -19
  17. package/lib/core/bootstrap.js +3 -34
  18. package/lib/core/domain/content-type/index.js +3 -7
  19. package/lib/core/domain/module/index.js +8 -6
  20. package/lib/core/domain/module/validation.js +1 -4
  21. package/lib/core/loaders/admin.js +2 -2
  22. package/lib/core/loaders/apis.js +7 -7
  23. package/lib/core/loaders/components.js +3 -5
  24. package/lib/core/loaders/index.js +1 -0
  25. package/lib/core/loaders/middlewares.js +23 -123
  26. package/lib/core/loaders/plugins/get-enabled-plugins.js +48 -14
  27. package/lib/core/loaders/plugins/index.js +30 -14
  28. package/lib/core/loaders/policies.js +1 -1
  29. package/lib/core/loaders/src-index.js +39 -0
  30. package/lib/core/registries/apis.js +2 -16
  31. package/lib/core/registries/content-types.js +50 -6
  32. package/lib/core/registries/controllers.d.ts +7 -0
  33. package/lib/core/registries/controllers.js +74 -3
  34. package/lib/core/registries/hooks.d.ts +20 -0
  35. package/lib/core/registries/hooks.js +87 -0
  36. package/lib/core/registries/middlewares.d.ts +5 -0
  37. package/lib/core/registries/middlewares.js +61 -2
  38. package/lib/core/registries/modules.js +3 -3
  39. package/lib/core/registries/plugins.js +2 -2
  40. package/lib/core/registries/policies.d.ts +9 -0
  41. package/lib/core/registries/policies.js +57 -6
  42. package/lib/core/registries/services.d.ts +7 -0
  43. package/lib/core/registries/services.js +71 -15
  44. package/lib/core-api/controller/collection-type.js +38 -11
  45. package/lib/core-api/controller/index.d.ts +25 -0
  46. package/lib/core-api/controller/index.js +30 -11
  47. package/lib/core-api/controller/single-type.js +26 -7
  48. package/lib/core-api/controller/transform.js +28 -3
  49. package/lib/core-api/routes/index.js +71 -0
  50. package/lib/core-api/service/collection-type.js +22 -27
  51. package/lib/core-api/service/index.d.ts +21 -0
  52. package/lib/core-api/service/index.js +9 -19
  53. package/lib/core-api/service/pagination.js +7 -2
  54. package/lib/core-api/service/single-type.js +17 -20
  55. package/lib/factories.d.ts +48 -0
  56. package/lib/factories.js +84 -0
  57. package/lib/index.d.ts +10 -31
  58. package/lib/index.js +5 -1
  59. package/lib/middlewares/body.js +33 -0
  60. package/lib/middlewares/compression.js +8 -0
  61. package/lib/middlewares/cors.js +58 -0
  62. package/lib/middlewares/errors.js +40 -0
  63. package/lib/middlewares/favicon.js +19 -0
  64. package/lib/middlewares/index.d.ts +5 -0
  65. package/lib/middlewares/index.js +30 -116
  66. package/lib/middlewares/ip.js +8 -0
  67. package/lib/middlewares/logger.js +27 -0
  68. package/lib/middlewares/powered-by.js +20 -0
  69. package/lib/middlewares/public/index.js +98 -73
  70. package/lib/middlewares/query.js +46 -0
  71. package/lib/middlewares/response-time.js +15 -0
  72. package/lib/middlewares/responses.js +19 -0
  73. package/lib/middlewares/security.js +51 -0
  74. package/lib/middlewares/session/index.js +6 -6
  75. package/lib/migrations/draft-publish.js +57 -0
  76. package/lib/services/auth/index.js +87 -0
  77. package/lib/services/core-store.js +64 -49
  78. package/lib/services/cron.js +54 -0
  79. package/lib/services/entity-service/attributes/index.js +31 -0
  80. package/lib/services/entity-service/attributes/transforms.js +20 -0
  81. package/lib/services/entity-service/components.js +39 -15
  82. package/lib/services/entity-service/index.d.ts +91 -0
  83. package/lib/services/entity-service/index.js +118 -60
  84. package/lib/services/entity-service/params.js +48 -81
  85. package/lib/services/entity-validator/index.js +76 -43
  86. package/lib/services/entity-validator/validators.js +129 -43
  87. package/lib/services/errors.js +77 -0
  88. package/lib/services/fs.js +1 -1
  89. package/lib/services/metrics/index.js +38 -36
  90. package/lib/services/server/admin-api.js +14 -0
  91. package/lib/services/server/api.js +36 -0
  92. package/lib/services/server/compose-endpoint.js +141 -0
  93. package/lib/services/server/content-api.js +16 -0
  94. package/lib/{server.js → services/server/http-server.js} +0 -0
  95. package/lib/services/server/index.js +127 -0
  96. package/lib/services/server/koa.js +64 -0
  97. package/lib/services/server/middleware.js +122 -0
  98. package/lib/services/server/policy.js +32 -0
  99. package/lib/services/server/register-middlewares.js +110 -0
  100. package/lib/services/server/register-routes.js +106 -0
  101. package/lib/services/server/routing.js +120 -0
  102. package/lib/services/webhook-runner.js +1 -1
  103. package/lib/utils/ee.js +3 -3
  104. package/lib/utils/get-dirs.js +17 -0
  105. package/lib/utils/index.js +2 -0
  106. package/lib/utils/signals.js +24 -0
  107. package/lib/utils/update-notifier/index.js +2 -1
  108. package/package.json +93 -93
  109. package/lib/core/app-configuration/load-functions.js +0 -28
  110. package/lib/core-api/index.js +0 -39
  111. package/lib/middlewares/boom/defaults.json +0 -5
  112. package/lib/middlewares/boom/index.js +0 -147
  113. package/lib/middlewares/cors/index.js +0 -66
  114. package/lib/middlewares/cron/defaults.json +0 -5
  115. package/lib/middlewares/cron/index.js +0 -43
  116. package/lib/middlewares/favicon/defaults.json +0 -7
  117. package/lib/middlewares/favicon/index.js +0 -32
  118. package/lib/middlewares/gzip/defaults.json +0 -6
  119. package/lib/middlewares/gzip/index.js +0 -19
  120. package/lib/middlewares/helmet/defaults.json +0 -18
  121. package/lib/middlewares/helmet/index.js +0 -9
  122. package/lib/middlewares/ip/defaults.json +0 -7
  123. package/lib/middlewares/ip/index.js +0 -25
  124. package/lib/middlewares/language/defaults.json +0 -9
  125. package/lib/middlewares/language/index.js +0 -40
  126. package/lib/middlewares/logger/defaults.json +0 -5
  127. package/lib/middlewares/logger/index.js +0 -37
  128. package/lib/middlewares/parser/defaults.json +0 -11
  129. package/lib/middlewares/parser/index.js +0 -72
  130. package/lib/middlewares/poweredBy/defaults.json +0 -5
  131. package/lib/middlewares/poweredBy/index.js +0 -16
  132. package/lib/middlewares/public/defaults.json +0 -8
  133. package/lib/middlewares/responseTime/defaults.json +0 -5
  134. package/lib/middlewares/responseTime/index.js +0 -25
  135. package/lib/middlewares/responses/defaults.json +0 -5
  136. package/lib/middlewares/responses/index.js +0 -18
  137. package/lib/middlewares/router/defaults.json +0 -7
  138. package/lib/middlewares/router/index.js +0 -72
  139. package/lib/middlewares/router/utils/compose-endpoint.js +0 -169
  140. 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,6 +1,6 @@
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');
@@ -8,6 +8,12 @@ const { isKebabCase } = require('@strapi/utils');
8
8
  const loadConfigFile = require('../../app-configuration/load-config-file');
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,6 +45,18 @@ 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');
@@ -49,21 +64,38 @@ const getEnabledPlugins = async strapi => {
49
64
 
50
65
  if (isStrapiPlugin(packageInfo)) {
51
66
  validatePluginName(packageInfo.strapi.name);
52
- installedPlugins[packageInfo.strapi.name] = toDetailedDeclaration({
53
- enabled: true,
54
- resolve: packagePath,
55
- });
67
+ installedPlugins[packageInfo.strapi.name] = {
68
+ ...toDetailedDeclaration({ enabled: true, resolve: packagePath }),
69
+ info: packageInfo.strapi,
70
+ };
56
71
  }
57
72
  }
58
73
 
59
74
  const declaredPlugins = {};
60
- const userPluginConfigPath = join(strapi.dir, 'config', 'plugins.js');
75
+ const userPluginConfigPath = join(strapi.dirs.config, 'plugins.js');
61
76
  const userPluginsConfig = existsSync(userPluginConfigPath)
62
77
  ? loadConfigFile(userPluginConfigPath)
63
78
  : {};
79
+
64
80
  _.forEach(userPluginsConfig, (declaration, pluginName) => {
65
81
  validatePluginName(pluginName);
66
- declaredPlugins[pluginName] = toDetailedDeclaration(declaration);
82
+
83
+ declaredPlugins[pluginName] = {
84
+ ...toDetailedDeclaration(declaration),
85
+ info: {},
86
+ };
87
+
88
+ const { pathToPlugin } = declaredPlugins[pluginName];
89
+
90
+ // for manually resolved plugins
91
+ if (pathToPlugin) {
92
+ const packagePath = join(pathToPlugin, 'package.json');
93
+ const packageInfo = require(packagePath);
94
+
95
+ if (isStrapiPlugin(packageInfo)) {
96
+ declaredPlugins[pluginName].info = packageInfo.strapi || {};
97
+ }
98
+ }
67
99
  });
68
100
 
69
101
  const declaredPluginsResolves = map(prop('pathToPlugin'), declaredPlugins);
@@ -71,10 +103,12 @@ const getEnabledPlugins = async strapi => {
71
103
  p => !declaredPluginsResolves.includes(p.pathToPlugin),
72
104
  installedPlugins
73
105
  );
106
+
74
107
  const enabledPlugins = pipe(
108
+ defaultsDeep(declaredPlugins),
75
109
  defaultsDeep(installedPluginsNotAlreadyUsed),
76
110
  pickBy(p => p.enabled)
77
- )(declaredPlugins);
111
+ )(internalPlugins);
78
112
 
79
113
  return enabledPlugins;
80
114
  };
@@ -1,7 +1,7 @@
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');
@@ -9,12 +9,12 @@ const loadFiles = require('../../../load/load-files');
9
9
  const getEnabledPlugins = require('./get-enabled-plugins');
10
10
 
11
11
  const defaultPlugin = {
12
- bootstrap: () => {},
13
- destroy: () => {},
14
- register: () => {},
12
+ bootstrap() {},
13
+ destroy() {},
14
+ register() {},
15
15
  config: {
16
16
  default: {},
17
- validator: () => {},
17
+ validator() {},
18
18
  },
19
19
  routes: [],
20
20
  controllers: {},
@@ -25,8 +25,8 @@ const defaultPlugin = {
25
25
  };
26
26
 
27
27
  const applyUserExtension = async plugins => {
28
- const extensionsDir = resolve(strapi.dir, 'extensions');
29
- if (!existsSync(extensionsDir)) {
28
+ const extensionsDir = strapi.dirs.extensions;
29
+ if (!(await fse.pathExists(extensionsDir))) {
30
30
  return;
31
31
  }
32
32
 
@@ -39,7 +39,11 @@ const applyUserExtension = async plugins => {
39
39
  for (const ctName in plugin.contentTypes) {
40
40
  const extendedSchema = get([pluginName, 'content-types', ctName, 'schema'], extendedSchemas);
41
41
  if (extendedSchema) {
42
- plugin.contentTypes[ctName].schema = extendedSchema;
42
+ plugin.contentTypes[ctName].schema = Object.assign(
43
+ {},
44
+ plugin.contentTypes[ctName].schema,
45
+ extendedSchema
46
+ );
43
47
  }
44
48
  }
45
49
  // second: execute strapi-server extension
@@ -61,9 +65,9 @@ const formatContentTypes = plugins => {
61
65
  }
62
66
  };
63
67
 
64
- const applyUserConfig = plugins => {
65
- const userPluginConfigPath = join(strapi.dir, 'config', 'plugins.js');
66
- const userPluginsConfig = existsSync(userPluginConfigPath)
68
+ const applyUserConfig = async plugins => {
69
+ const userPluginConfigPath = join(strapi.dirs.config, 'plugins.js');
70
+ const userPluginsConfig = (await fse.pathExists(userPluginConfigPath))
67
71
  ? loadConfigFile(userPluginConfigPath)
68
72
  : {};
69
73
 
@@ -87,15 +91,27 @@ const applyUserConfig = plugins => {
87
91
 
88
92
  const loadPlugins = async strapi => {
89
93
  const plugins = {};
94
+
90
95
  const enabledPlugins = await getEnabledPlugins(strapi);
91
96
 
97
+ strapi.config.set('enabledPlugins', enabledPlugins);
98
+
92
99
  for (const pluginName in enabledPlugins) {
93
100
  const enabledPlugin = enabledPlugins[pluginName];
94
- const pluginServer = loadConfigFile(join(enabledPlugin.pathToPlugin, 'strapi-server.js'));
101
+
102
+ const serverEntrypointPath = join(enabledPlugin.pathToPlugin, 'strapi-server.js');
103
+
104
+ // only load plugins with a server entrypoint
105
+ if (!(await fse.pathExists(serverEntrypointPath))) {
106
+ continue;
107
+ }
108
+
109
+ const pluginServer = loadConfigFile(serverEntrypointPath);
95
110
  plugins[pluginName] = defaultsDeep(defaultPlugin, pluginServer);
96
111
  }
112
+
97
113
  // TODO: validate plugin format
98
- applyUserConfig(plugins);
114
+ await applyUserConfig(plugins);
99
115
  await applyUserExtension(plugins);
100
116
  formatContentTypes(plugins);
101
117
 
@@ -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;
@@ -3,20 +3,80 @@
3
3
  const { pickBy, has } = require('lodash/fp');
4
4
  const { addNamespace, hasNamespace } = require('../utils');
5
5
 
6
+ /**
7
+ * @typedef {import('./controllers').Controller} Controller
8
+ * @typedef {import('./controllers').ControllerFactory} ControllerFactory
9
+ */
10
+
6
11
  const controllersRegistry = () => {
7
12
  const controllers = {};
13
+ const instances = {};
8
14
 
9
15
  return {
16
+ /**
17
+ * Returns this list of registered controllers uids
18
+ * @returns {string[]}
19
+ */
20
+ keys() {
21
+ return Object.keys(controllers);
22
+ },
23
+
24
+ /**
25
+ * Returns the instance of a controller. Instantiate the controller if not already done
26
+ * @param {string} uid
27
+ * @returns {Controller}
28
+ */
10
29
  get(uid) {
11
- return controllers[uid];
30
+ if (instances[uid]) {
31
+ return instances[uid];
32
+ }
33
+
34
+ const controller = controllers[uid];
35
+
36
+ if (controller) {
37
+ instances[uid] = typeof controller === 'function' ? controller({ strapi }) : controller;
38
+ return instances[uid];
39
+ }
12
40
  },
41
+
42
+ /**
43
+ * Returns a map with all the controller in a namespace
44
+ * @param {string} namespace
45
+ * @returns {{ [key: string]: Controller }}
46
+ */
13
47
  getAll(namespace) {
14
- return pickBy((_, uid) => hasNamespace(uid, namespace))(controllers);
48
+ const filteredControllers = pickBy((_, uid) => hasNamespace(uid, namespace))(controllers);
49
+
50
+ const map = {};
51
+ for (const uid in filteredControllers) {
52
+ Object.defineProperty(map, uid, {
53
+ enumerable: true,
54
+ get: () => {
55
+ return this.get(uid);
56
+ },
57
+ });
58
+ }
59
+
60
+ return map;
15
61
  },
62
+
63
+ /**
64
+ * Registers a controller
65
+ * @param {string} uid
66
+ * @param {Controller} controller
67
+ */
16
68
  set(uid, value) {
17
69
  controllers[uid] = value;
70
+ delete instances[uid];
18
71
  return this;
19
72
  },
73
+
74
+ /**
75
+ * Registers a map of controllers for a specific namespace
76
+ * @param {string} namespace
77
+ * @param {{ [key: string]: Controller|ControllerFactory }} newControllers
78
+ * @returns
79
+ */
20
80
  add(namespace, newControllers) {
21
81
  for (const controllerName in newControllers) {
22
82
  const controller = newControllers[controllerName];
@@ -27,15 +87,26 @@ const controllersRegistry = () => {
27
87
  }
28
88
  controllers[uid] = controller;
29
89
  }
90
+
30
91
  return this;
31
92
  },
93
+
94
+ /**
95
+ * Wraps a controller to extend it
96
+ * @param {string} uid
97
+ * @param {(controller: Controller) => Controller} extendFn
98
+ */
32
99
  extend(controllerUID, extendFn) {
33
100
  const currentController = this.get(controllerUID);
101
+
34
102
  if (!currentController) {
35
103
  throw new Error(`Controller ${controllerUID} doesn't exist`);
36
104
  }
105
+
37
106
  const newController = extendFn(currentController);
38
- controllers[controllerUID] = newController;
107
+ instances[controllerUID] = newController;
108
+
109
+ return this;
39
110
  },
40
111
  };
41
112
  };
@@ -0,0 +1,20 @@
1
+
2
+ type Handler = (context: any) => any;
3
+
4
+ type AsyncHook = {
5
+ handlers: Handler[];
6
+ register(handler: Handler): this;
7
+ delete(handler: Handler): this;
8
+ call(): Promise<void>;
9
+ };
10
+
11
+
12
+ type SyncHook = {
13
+ get handlers(): Handler[];
14
+ register(handler: Handler): this;
15
+ delete(handler: Handler): this;
16
+ call(): void;
17
+ };
18
+
19
+
20
+ export type Hook = AsyncHook|SyncHook