@strapi/strapi 4.2.0-beta.0 → 4.2.0-beta.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 (40) hide show
  1. package/README.md +2 -3
  2. package/bin/strapi.js +7 -0
  3. package/lib/Strapi.js +22 -4
  4. package/lib/commands/admin-create.js +15 -1
  5. package/lib/commands/admin-reset.js +15 -1
  6. package/lib/commands/build.js +4 -5
  7. package/lib/commands/builders/admin.js +4 -3
  8. package/lib/commands/builders/typescript.js +3 -6
  9. package/lib/commands/configurationDump.js +14 -1
  10. package/lib/commands/configurationRestore.js +16 -1
  11. package/lib/commands/console.js +16 -2
  12. package/lib/commands/develop.js +10 -4
  13. package/lib/commands/opt-out-telemetry.js +83 -0
  14. package/lib/commands/routes/list.js +15 -1
  15. package/lib/commands/start.js +12 -2
  16. package/lib/commands/watchAdmin.js +3 -4
  17. package/lib/core/app-configuration/index.js +5 -3
  18. package/lib/core/bootstrap.js +9 -1
  19. package/lib/core/loaders/apis.js +13 -9
  20. package/lib/core/loaders/index.js +1 -0
  21. package/lib/core/loaders/plugins/get-enabled-plugins.js +1 -6
  22. package/lib/core/loaders/sanitizers.js +5 -0
  23. package/lib/core/registries/policies.d.ts +1 -1
  24. package/lib/core/registries/sanitizers.js +26 -0
  25. package/lib/core-api/controller/index.d.ts +14 -9
  26. package/lib/core-api/service/index.d.ts +10 -9
  27. package/lib/factories.d.ts +21 -17
  28. package/lib/index.d.ts +7 -6
  29. package/lib/middlewares/body.js +38 -10
  30. package/lib/middlewares/public/index.js +1 -1
  31. package/lib/middlewares/session.js +3 -1
  32. package/lib/services/entity-validator/validators.js +3 -1
  33. package/lib/services/metrics/index.js +7 -1
  34. package/lib/services/metrics/sender.js +7 -0
  35. package/lib/services/server/index.js +1 -1
  36. package/lib/types/strapi.d.ts +291 -0
  37. package/lib/types/utils.d.ts +1 -0
  38. package/lib/utils/get-dirs.js +5 -4
  39. package/lib/utils/update-notifier/index.js +2 -2
  40. package/package.json +14 -13
package/README.md CHANGED
@@ -114,8 +114,7 @@ For general help using Strapi, please refer to [the official Strapi documentatio
114
114
  - [Discord](https://discord.strapi.io) (For live discussion with the Community and Strapi team)
115
115
  - [GitHub](https://github.com/strapi/strapi) (Bug reports, Contributions)
116
116
  - [Community Forum](https://forum.strapi.io) (Questions and Discussions)
117
- - [Academy](https://academy.strapi.io) (Learn the fundamentals of Strapi)
118
- - [ProductBoard](https://portal.productboard.com/strapi/tabs/2-under-consideration) (Roadmap, Feature requests)
117
+ - [Roadmap & Feature Requests](https://feedback.strapi.io/)
119
118
  - [Twitter](https://twitter.com/strapijs) (Get the news fast)
120
119
  - [Facebook](https://www.facebook.com/Strapi-616063331867161)
121
120
  - [YouTube Channel](https://www.youtube.com/strapi) (Learn from Video Tutorials)
@@ -126,7 +125,7 @@ Follow our [migration guides](https://docs.strapi.io/developer-docs/latest/updat
126
125
 
127
126
  ## Roadmap
128
127
 
129
- Check out our [roadmap](https://portal.productboard.com/strapi) to get informed of the latest features released and the upcoming ones. You may also give us insights and vote for a specific feature.
128
+ Check out our [roadmap](https://feedback.strapi.io/) to get informed of the latest features released and the upcoming ones. You may also give us insights and vote for a specific feature.
130
129
 
131
130
  ## Documentation
132
131
 
package/bin/strapi.js CHANGED
@@ -95,6 +95,7 @@ program
95
95
  .option('--dbssl <dbssl>', 'Database SSL')
96
96
  .option('--dbfile <dbfile>', 'Database file path for sqlite')
97
97
  .option('--dbforce', 'Allow overwriting existing database content')
98
+ .option('-ts, --typescript', 'Create a typescript project')
98
99
  .description('Create a new application')
99
100
  .action(require('../lib/commands/new'));
100
101
 
@@ -227,4 +228,10 @@ program
227
228
  .description('List all the application controllers')
228
229
  .action(getLocalScript('controllers/list'));
229
230
 
231
+ // `$ strapi opt-out-telemetry`
232
+ program
233
+ .command('telemetry:disable')
234
+ .description('Stop Strapi from sending anonymous telemetry and metadata')
235
+ .action(getLocalScript('opt-out-telemetry'));
236
+
230
237
  program.parseAsync(process.argv);
package/lib/Strapi.js CHANGED
@@ -38,10 +38,15 @@ const apisRegistry = require('./core/registries/apis');
38
38
  const bootstrap = require('./core/bootstrap');
39
39
  const loaders = require('./core/loaders');
40
40
  const { destroyOnSignal } = require('./utils/signals');
41
+ const sanitizersRegistry = require('./core/registries/sanitizers');
41
42
 
42
43
  // TODO: move somewhere else
43
44
  const draftAndPublishSync = require('./migrations/draft-publish');
44
45
 
46
+ /**
47
+ * A map of all the available Strapi lifecycles
48
+ * @type {import('@strapi/strapi').Core.Lifecycles}
49
+ */
45
50
  const LIFECYCLES = {
46
51
  REGISTER: 'register',
47
52
  BOOTSTRAP: 'bootstrap',
@@ -65,18 +70,18 @@ const resolveWorkingDirectories = opts => {
65
70
  const appDir = opts.appDir ? path.resolve(cwd, opts.appDir) : cwd;
66
71
  const distDir = opts.distDir ? path.resolve(cwd, opts.distDir) : appDir;
67
72
 
68
- return { appDir, distDir };
73
+ return { app: appDir, dist: distDir };
69
74
  };
70
75
 
76
+ /** @implements {import('@strapi/strapi').Strapi} */
71
77
  class Strapi {
72
78
  constructor(opts = {}) {
73
79
  destroyOnSignal(this);
74
80
 
75
- // Create a mapping of every useful directory (both for the app and dist directories)
76
- this.dirs = utils.getDirs(resolveWorkingDirectories(opts));
81
+ const rootDirs = resolveWorkingDirectories(opts);
77
82
 
78
83
  // Load the app configuration from the dist directory
79
- const appConfig = loadConfiguration(this.dirs.dist.root, opts);
84
+ const appConfig = loadConfiguration({ appDir: rootDirs.app, distDir: rootDirs.dist }, opts);
80
85
 
81
86
  // Instanciate the Strapi container
82
87
  this.container = createContainer(this);
@@ -93,6 +98,10 @@ class Strapi {
93
98
  this.container.register('plugins', pluginsRegistry(this));
94
99
  this.container.register('apis', apisRegistry(this));
95
100
  this.container.register('auth', createAuth(this));
101
+ this.container.register('sanitizers', sanitizersRegistry(this));
102
+
103
+ // Create a mapping of every useful directory (for the app, dist and static directories)
104
+ this.dirs = utils.getDirs(rootDirs, { strapi: this });
96
105
 
97
106
  // Strapi state management variables
98
107
  this.isLoaded = false;
@@ -188,6 +197,10 @@ class Strapi {
188
197
  return this.container.get('auth');
189
198
  }
190
199
 
200
+ get sanitizers() {
201
+ return this.container.get('sanitizers');
202
+ }
203
+
191
204
  async start() {
192
205
  try {
193
206
  if (!this.isLoaded) {
@@ -335,6 +348,10 @@ class Strapi {
335
348
  this.app = await loaders.loadSrcIndex(this);
336
349
  }
337
350
 
351
+ async loadSanitizers() {
352
+ await loaders.loadSanitizers(this);
353
+ }
354
+
338
355
  registerInternalHooks() {
339
356
  this.container.get('hooks').set('strapi::content-types.beforeSync', createAsyncParallelHook());
340
357
  this.container.get('hooks').set('strapi::content-types.afterSync', createAsyncParallelHook());
@@ -346,6 +363,7 @@ class Strapi {
346
363
  async register() {
347
364
  await Promise.all([
348
365
  this.loadApp(),
366
+ this.loadSanitizers(),
349
367
  this.loadPlugins(),
350
368
  this.loadAdmin(),
351
369
  this.loadAPIs(),
@@ -3,6 +3,7 @@
3
3
  const { yup } = require('@strapi/utils');
4
4
  const _ = require('lodash');
5
5
  const inquirer = require('inquirer');
6
+ const tsUtils = require('@strapi/typescript-utils');
6
7
  const strapi = require('../index');
7
8
 
8
9
  const emailValidator = yup
@@ -90,7 +91,20 @@ module.exports = async function(cmdOptions = {}) {
90
91
  };
91
92
 
92
93
  async function createAdmin({ email, password, firstname, lastname }) {
93
- const app = await strapi().load();
94
+ const appDir = process.cwd();
95
+
96
+ const isTSProject = await tsUtils.isUsingTypeScript(appDir);
97
+ const outDir = await tsUtils.resolveOutDir(appDir);
98
+
99
+ if (isTSProject)
100
+ await tsUtils.compile(appDir, {
101
+ watch: false,
102
+ configOptions: { options: { incremental: true } },
103
+ });
104
+
105
+ const distDir = isTSProject ? outDir : appDir;
106
+
107
+ const app = await strapi({ appDir, distDir }).load();
94
108
 
95
109
  const user = await app.admin.services.user.exists({ email });
96
110
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  const _ = require('lodash');
4
4
  const inquirer = require('inquirer');
5
+ const tsUtils = require('@strapi/typescript-utils');
5
6
  const strapi = require('../index');
6
7
 
7
8
  const promptQuestions = [
@@ -42,7 +43,20 @@ module.exports = async function(cmdOptions = {}) {
42
43
  };
43
44
 
44
45
  async function changePassword({ email, password }) {
45
- const app = await strapi().load();
46
+ const appDir = process.cwd();
47
+
48
+ const isTSProject = await tsUtils.isUsingTypeScript(appDir);
49
+ const outDir = await tsUtils.resolveOutDir(appDir);
50
+
51
+ if (isTSProject)
52
+ await tsUtils.compile(appDir, {
53
+ watch: false,
54
+ configOptions: { options: { incremental: true } },
55
+ });
56
+
57
+ const distDir = isTSProject ? outDir : appDir;
58
+
59
+ const app = await strapi({ appDir, distDir }).load();
46
60
 
47
61
  await app.admin.services.user.resetPasswordByEmail(email, password);
48
62
 
@@ -1,5 +1,4 @@
1
1
  'use strict';
2
- const path = require('path');
3
2
 
4
3
  const tsUtils = require('@strapi/typescript-utils');
5
4
  const { buildAdmin, buildTypeScript } = require('./builders');
@@ -11,20 +10,20 @@ module.exports = async ({ optimization, forceBuild = true }) => {
11
10
  let buildDestDir = process.cwd();
12
11
  const srcDir = process.cwd();
13
12
 
14
- const isTSProject = await tsUtils.isTypeScriptProject(srcDir);
13
+ const useTypeScriptServer = await tsUtils.isUsingTypeScript(srcDir);
14
+ const outDir = await tsUtils.resolveOutDir(srcDir);
15
15
 
16
16
  // Typescript
17
- if (isTSProject) {
17
+ if (useTypeScriptServer) {
18
18
  await buildTypeScript({ srcDir, watch: false });
19
19
 
20
20
  // Update the dir path for the next steps
21
- buildDestDir = path.join(srcDir, 'dist');
21
+ buildDestDir = outDir;
22
22
  }
23
23
 
24
24
  await buildAdmin({
25
25
  buildDestDir,
26
26
  forceBuild,
27
- isTSProject,
28
27
  optimization,
29
28
  srcDir,
30
29
  });
@@ -10,10 +10,12 @@ const addSlash = require('../../utils/addSlash');
10
10
  const strapi = require('../../index');
11
11
  const getEnabledPlugins = require('../../core/loaders/plugins/get-enabled-plugins');
12
12
 
13
- module.exports = async ({ buildDestDir, forceBuild = true, isTSProject, optimization, srcDir }) => {
13
+ module.exports = async ({ buildDestDir, forceBuild = true, optimization, srcDir }) => {
14
14
  const strapiInstance = strapi({
15
- // TODO check if this is working @convly
15
+ // Directories
16
+ appDir: srcDir,
16
17
  distDir: buildDestDir,
18
+ // Options
17
19
  autoReload: true,
18
20
  serveAdminPanel: false,
19
21
  });
@@ -46,7 +48,6 @@ module.exports = async ({ buildDestDir, forceBuild = true, isTSProject, optimiza
46
48
  backend: serverUrl,
47
49
  adminPath: addSlash(adminPath),
48
50
  },
49
- useTypeScript: isTSProject,
50
51
  })
51
52
  .then(() => {
52
53
  console.log('Admin UI built successfully');
@@ -5,8 +5,8 @@ const fs = require('fs-extra');
5
5
  const tsUtils = require('@strapi/typescript-utils');
6
6
 
7
7
  const cleanupDistDirectory = async distDir => {
8
- if (!await fs.pathExists(distDir)){
9
- return
8
+ if (!(await fs.pathExists(distDir))) {
9
+ return;
10
10
  }
11
11
 
12
12
  const dirContent = await fs.readdir(distDir);
@@ -14,21 +14,18 @@ const cleanupDistDirectory = async distDir => {
14
14
  // Ignore the admin build folder
15
15
  .filter(filename => filename !== 'build');
16
16
 
17
-
18
17
  for (const filename of validFilenames) {
19
18
  await fs.remove(path.resolve(distDir, filename));
20
19
  }
21
20
  };
22
21
 
23
22
  module.exports = async ({ srcDir, distDir, watch = false }) => {
24
- const isTSProject = await tsUtils.isTypeScriptProject(srcDir);
23
+ const isTSProject = await tsUtils.isUsingTypeScript(srcDir);
25
24
 
26
25
  if (!isTSProject) {
27
26
  throw new Error(`tsconfig file not found in ${srcDir}`);
28
27
  }
29
28
 
30
-
31
-
32
29
  await cleanupDistDirectory(distDir);
33
30
 
34
31
  return tsUtils.compile(srcDir, { watch });
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const fs = require('fs');
4
+ const tsUtils = require('@strapi/typescript-utils');
4
5
  const strapi = require('../index');
5
6
 
6
7
  const CHUNK_SIZE = 100;
@@ -12,7 +13,19 @@ const CHUNK_SIZE = 100;
12
13
  module.exports = async function({ file: filePath, pretty }) {
13
14
  const output = filePath ? fs.createWriteStream(filePath) : process.stdout;
14
15
 
15
- const app = await strapi().load();
16
+ const appDir = process.cwd();
17
+
18
+ const isTSProject = await tsUtils.isUsingTypeScript(appDir);
19
+ const outDir = await tsUtils.resolveOutDir(appDir);
20
+ if (isTSProject)
21
+ await tsUtils.compile(appDir, {
22
+ watch: false,
23
+ configOptions: { options: { incremental: true } },
24
+ });
25
+
26
+ const distDir = isTSProject ? outDir : appDir;
27
+
28
+ const app = await strapi({ appDir, distDir }).load();
16
29
 
17
30
  const count = await app.query('strapi::core-store').count();
18
31
 
@@ -2,6 +2,8 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const _ = require('lodash');
5
+ const tsUtils = require('@strapi/typescript-utils');
6
+
5
7
  const strapi = require('../index');
6
8
 
7
9
  /**
@@ -12,7 +14,20 @@ const strapi = require('../index');
12
14
  module.exports = async function({ file: filePath, strategy = 'replace' }) {
13
15
  const input = filePath ? fs.readFileSync(filePath) : await readStdin(process.stdin);
14
16
 
15
- const app = await strapi().load();
17
+ const appDir = process.cwd();
18
+
19
+ const isTSProject = await tsUtils.isUsingTypeScript(appDir);
20
+ const outDir = await tsUtils.resolveOutDir(appDir);
21
+
22
+ if (isTSProject)
23
+ await tsUtils.compile(appDir, {
24
+ watch: false,
25
+ configOptions: { options: { incremental: true } },
26
+ });
27
+
28
+ const distDir = isTSProject ? outDir : appDir;
29
+
30
+ const app = await strapi({ appDir, distDir }).load();
16
31
 
17
32
  let dataToImport;
18
33
  try {
@@ -1,14 +1,28 @@
1
1
  'use strict';
2
2
 
3
3
  const REPL = require('repl');
4
+ const tsUtils = require('@strapi/typescript-utils');
5
+
4
6
  const strapi = require('../index');
5
7
 
6
8
  /**
7
9
  * `$ strapi console`
8
10
  */
9
- module.exports = () => {
11
+ module.exports = async () => {
10
12
  // Now load up the Strapi framework for real.
11
- const app = strapi();
13
+ const appDir = process.cwd();
14
+ const isTSProject = await tsUtils.isUsingTypeScript(appDir);
15
+ const outDir = await tsUtils.resolveOutDir(appDir);
16
+
17
+ if (isTSProject)
18
+ await tsUtils.compile(appDir, {
19
+ watch: false,
20
+ configOptions: { options: { incremental: true } },
21
+ });
22
+
23
+ const distDir = isTSProject ? outDir : appDir;
24
+
25
+ const app = await strapi({ appDir, distDir }).load();
12
26
 
13
27
  app.start().then(() => {
14
28
  const repl = REPL.start(app.config.info.name + ' > ' || 'strapi > '); // eslint-disable-line prefer-template
@@ -6,6 +6,7 @@ const fs = require('fs-extra');
6
6
  const chokidar = require('chokidar');
7
7
  const execa = require('execa');
8
8
  const { getOr } = require('lodash/fp');
9
+ const { joinBy } = require('@strapi/utils');
9
10
  const tsUtils = require('@strapi/typescript-utils');
10
11
 
11
12
  const loadConfiguration = require('../core/app-configuration');
@@ -20,8 +21,9 @@ const { buildTypeScript, buildAdmin } = require('./builders');
20
21
  module.exports = async function({ build, watchAdmin, polling, browser }) {
21
22
  const appDir = process.cwd();
22
23
 
23
- const isTSProject = await tsUtils.isTypeScriptProject(appDir);
24
- const distDir = isTSProject ? path.join(appDir, 'dist') : appDir;
24
+ const isTSProject = await tsUtils.isUsingTypeScript(appDir);
25
+ const outDir = await tsUtils.resolveOutDir(appDir);
26
+ const distDir = isTSProject ? outDir : appDir;
25
27
 
26
28
  try {
27
29
  if (cluster.isMaster || cluster.isPrimary) {
@@ -49,7 +51,7 @@ const primaryProcess = async ({ distDir, appDir, build, isTSProject, watchAdmin,
49
51
  await buildTypeScript({ srcDir: appDir, distDir, watch: false });
50
52
  }
51
53
 
52
- const config = loadConfiguration(distDir);
54
+ const config = loadConfiguration({ appDir, distDir });
53
55
  const serveAdminPanel = getOr(true, 'admin.serveAdminPanel')(config);
54
56
 
55
57
  const buildExists = fs.existsSync(path.join(distDir, 'build'));
@@ -60,7 +62,6 @@ const primaryProcess = async ({ distDir, appDir, build, isTSProject, watchAdmin,
60
62
  await buildAdmin({
61
63
  buildDestDir: distDir,
62
64
  forceBuild: false,
63
- isTSProject,
64
65
  optimization: false,
65
66
  srcDir: appDir,
66
67
  });
@@ -157,6 +158,9 @@ function watchFileChanges({ appDir, strapiInstance, watchIgnoreFiles, polling })
157
158
  /tmp/,
158
159
  '**/src/admin/**',
159
160
  '**/src/plugins/**/admin/**',
161
+ // FIXME pass the plugin path to the strapiAdmin.build and strapiAdmin.watch in order to stop copying
162
+ // the FE files when using TS
163
+ '**/dist/src/plugins/test/admin/**',
160
164
  '**/documentation',
161
165
  '**/documentation/**',
162
166
  '**/node_modules',
@@ -167,6 +171,8 @@ function watchFileChanges({ appDir, strapiInstance, watchIgnoreFiles, polling })
167
171
  '**/index.html',
168
172
  '**/public',
169
173
  '**/public/**',
174
+ strapiInstance.dirs.static.public,
175
+ joinBy('/', strapiInstance.dirs.static.public, '**'),
170
176
  '**/*.db*',
171
177
  '**/exports/**',
172
178
  '**/dist/**',
@@ -0,0 +1,83 @@
1
+ 'use strict';
2
+
3
+ const { resolve } = require('path');
4
+ const fse = require('fs-extra');
5
+ const chalk = require('chalk');
6
+ const fetch = require('node-fetch');
7
+ const machineID = require('../utils/machine-id');
8
+
9
+ const readPackageJSON = async path => {
10
+ try {
11
+ const packageObj = await fse.readJson(path);
12
+ const uuid = packageObj.strapi ? packageObj.strapi.uuid : null;
13
+
14
+ return { uuid, packageObj };
15
+ } catch (err) {
16
+ console.error(`${chalk.red('Error')}: ${err.message}`);
17
+ }
18
+ };
19
+
20
+ const writePackageJSON = async (path, file, spacing) => {
21
+ try {
22
+ await fse.writeJson(path, file, { spaces: spacing });
23
+ return true;
24
+ } catch (err) {
25
+ console.error(`${chalk.red('Error')}: ${err.message}`);
26
+ }
27
+ };
28
+
29
+ const sendEvent = async uuid => {
30
+ try {
31
+ await fetch('https://analytics.strapi.io/track', {
32
+ method: 'POST',
33
+ body: JSON.stringify({
34
+ event: 'didOptOutTelemetry',
35
+ uuid,
36
+ deviceId: machineID(),
37
+ }),
38
+ headers: { 'Content-Type': 'application/json' },
39
+ });
40
+ } catch (e) {
41
+ //...
42
+ }
43
+ };
44
+
45
+ module.exports = async function optOutTelemetry() {
46
+ const packageJSONPath = resolve(process.cwd(), 'package.json');
47
+ const exists = await fse.pathExists(packageJSONPath);
48
+
49
+ if (!exists) {
50
+ console.log(`${chalk.yellow('Warning')}: could not find package.json`);
51
+ process.exit(0);
52
+ }
53
+
54
+ const { uuid, packageObj } = await readPackageJSON(packageJSONPath);
55
+
56
+ if ((packageObj.strapi && packageObj.strapi.telemetryDisabled) || !uuid) {
57
+ console.log(`${chalk.yellow('Warning:')} telemetry is already disabled`);
58
+ process.exit(0);
59
+ }
60
+
61
+ const updatedPackageJSON = {
62
+ ...packageObj,
63
+ strapi: {
64
+ ...packageObj.strapi,
65
+ telemetryDisabled: true,
66
+ },
67
+ };
68
+
69
+ const write = await writePackageJSON(packageJSONPath, updatedPackageJSON, 2);
70
+
71
+ if (!write) {
72
+ console.log(
73
+ `${chalk.yellow(
74
+ 'Warning'
75
+ )}: There has been an error, please set "telemetryDisabled": true in the "strapi" object of your package.json manually.`
76
+ );
77
+ process.exit(0);
78
+ }
79
+
80
+ await sendEvent(uuid);
81
+ console.log(`${chalk.green('Successfully opted out of Strapi telemetry')}`);
82
+ process.exit(0);
83
+ };
@@ -3,11 +3,25 @@
3
3
  const CLITable = require('cli-table3');
4
4
  const chalk = require('chalk');
5
5
  const { toUpper } = require('lodash/fp');
6
+ const tsUtils = require('@strapi/typescript-utils');
6
7
 
7
8
  const strapi = require('../../index');
8
9
 
9
10
  module.exports = async function() {
10
- const app = await strapi().load();
11
+ const appDir = process.cwd();
12
+
13
+ const isTSProject = await tsUtils.isUsingTypeScript(appDir);
14
+ const outDir = await tsUtils.resolveOutDir(appDir);
15
+
16
+ if (isTSProject)
17
+ await tsUtils.compile(appDir, {
18
+ watch: false,
19
+ configOptions: { options: { incremental: true } },
20
+ });
21
+
22
+ const distDir = isTSProject ? outDir : appDir;
23
+
24
+ const app = await strapi({ appDir, distDir }).load();
11
25
 
12
26
  const list = app.server.listRoutes();
13
27
 
@@ -1,8 +1,18 @@
1
1
  'use strict';
2
-
2
+ const fs = require('fs');
3
+ const tsUtils = require('@strapi/typescript-utils');
3
4
  const strapi = require('../index');
4
5
 
5
6
  /**
6
7
  * `$ strapi start`
7
8
  */
8
- module.exports = distDir => strapi({ distDir }).start();
9
+ module.exports = async specifiedDir => {
10
+ const appDir = process.cwd();
11
+ const isTSProject = await tsUtils.isUsingTypeScript(appDir);
12
+ const outDir = await tsUtils.resolveOutDir(appDir);
13
+ const buildDirExists = fs.existsSync(outDir);
14
+ if (isTSProject && !buildDirExists) throw new Error(`${outDir} directory not found. Please run the build command before starting your application`);
15
+ const distDir = isTSProject && !specifiedDir ? outDir : specifiedDir;
16
+
17
+ strapi({ distDir }).start();
18
+ };
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
 
3
- const path = require('path');
4
3
  const strapiAdmin = require('@strapi/admin');
5
4
  const tsUtils = require('@strapi/typescript-utils');
6
5
  const { getConfigUrls, getAbsoluteServerUrl } = require('@strapi/utils');
@@ -12,8 +11,9 @@ const strapi = require('../index');
12
11
  module.exports = async function({ browser }) {
13
12
  const currentDirectory = process.cwd();
14
13
 
15
- const isTSProject = await tsUtils.isTypeScriptProject(currentDirectory);
16
- const buildDestDir = isTSProject ? path.join(currentDirectory, 'dist') : currentDirectory;
14
+ const isTSProject = await tsUtils.isUsingTypeScript(currentDirectory);
15
+ const outDir = await tsUtils.resolveOutDir(currentDirectory);
16
+ const buildDestDir = isTSProject ? outDir : currentDirectory;
17
17
 
18
18
  const strapiInstance = strapi({
19
19
  distDir: buildDestDir,
@@ -41,6 +41,5 @@ module.exports = async function({ browser }) {
41
41
  backend: backendURL,
42
42
  adminPath: addSlash(adminPath),
43
43
  },
44
- useTypeScript: isTSProject,
45
44
  });
46
45
  };
@@ -21,6 +21,7 @@ const defaultConfig = {
21
21
  proxy: false,
22
22
  cron: { enabled: false },
23
23
  admin: { autoOpen: false },
24
+ dirs: { public: './public' },
24
25
  },
25
26
  admin: {},
26
27
  api: {
@@ -30,12 +31,13 @@ const defaultConfig = {
30
31
  },
31
32
  };
32
33
 
33
- module.exports = (dir, initialConfig = {}) => {
34
+ module.exports = (dirs, initialConfig = {}) => {
35
+ const { appDir, distDir } = dirs;
34
36
  const { autoReload = false, serveAdminPanel = true } = initialConfig;
35
37
 
36
- const pkgJSON = require(path.resolve(dir, 'package.json'));
38
+ const pkgJSON = require(path.resolve(appDir, 'package.json'));
37
39
 
38
- const configDir = path.resolve(dir || process.cwd(), 'config');
40
+ const configDir = path.resolve(distDir || process.cwd(), 'config');
39
41
 
40
42
  const rootConfig = {
41
43
  launchedAt: Date.now(),
@@ -1,8 +1,9 @@
1
1
  'use strict';
2
2
 
3
3
  const { getConfigUrls } = require('@strapi/utils');
4
+ const fse = require('fs-extra');
4
5
 
5
- module.exports = function({ strapi }) {
6
+ module.exports = async function({ strapi }) {
6
7
  strapi.config.port = strapi.config.get('server.port') || strapi.config.port;
7
8
  strapi.config.host = strapi.config.get('server.host') || strapi.config.host;
8
9
 
@@ -22,4 +23,11 @@ module.exports = function({ strapi }) {
22
23
  if (!shouldServeAdmin) {
23
24
  strapi.config.serveAdminPanel = false;
24
25
  }
26
+
27
+ // ensure public repository exists
28
+ if (!(await fse.pathExists(strapi.dirs.static.public))) {
29
+ throw new Error(
30
+ `The public folder (${strapi.dirs.static.public}) doesn't exist or is not accessible. Please make sure it exists.`
31
+ );
32
+ }
25
33
  };
@@ -7,31 +7,35 @@ const fse = require('fs-extra');
7
7
  const { isKebabCase } = require('@strapi/utils');
8
8
  const { importDefault } = require('../../utils');
9
9
 
10
- // to handle names with numbers in it we first check if it is already in kebabCase
11
- const normalizeName = name => (isKebabCase(name) ? name : _.kebabCase(name));
12
-
13
10
  const DEFAULT_CONTENT_TYPE = {
14
11
  schema: {},
15
12
  actions: {},
16
13
  lifecycles: {},
17
14
  };
18
15
 
16
+ // to handle names with numbers in it we first check if it is already in kebabCase
17
+ const normalizeName = name => (isKebabCase(name) ? name : _.kebabCase(name));
18
+
19
+ const isDirectory = fd => fd.isDirectory();
20
+ const isDotFile = fd => fd.name.startsWith('.');
21
+
19
22
  module.exports = async strapi => {
20
23
  if (!existsSync(strapi.dirs.dist.api)) {
21
24
  return;
22
25
  }
23
26
 
24
- const apisFDs = await fse.readdir(strapi.dirs.dist.api, { withFileTypes: true });
27
+ const apisFDs = await (await fse.readdir(strapi.dirs.dist.api, { withFileTypes: true }))
28
+ .filter(isDirectory)
29
+ .filter(_.negate(isDotFile));
30
+
25
31
  const apis = {};
26
32
 
27
33
  // only load folders
28
34
  for (const apiFD of apisFDs) {
29
- if (apiFD.isDirectory()) {
30
- const apiName = normalizeName(apiFD.name);
31
- const api = await loadAPI(join(strapi.dirs.dist.api, apiFD.name));
35
+ const apiName = normalizeName(apiFD.name);
36
+ const api = await loadAPI(join(strapi.dirs.dist.api, apiFD.name));
32
37
 
33
- apis[apiName] = api;
34
- }
38
+ apis[apiName] = api;
35
39
  }
36
40
 
37
41
  validateContentTypesUnicity(apis);
@@ -8,4 +8,5 @@ module.exports = {
8
8
  loadPolicies: require('./policies'),
9
9
  loadPlugins: require('./plugins'),
10
10
  loadAdmin: require('./admin'),
11
+ loadSanitizers: require('./sanitizers'),
11
12
  };